Merge d69eed99c5
into 88fce6a140
This commit is contained in:
commit
ec451b07ac
|
@ -12,7 +12,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d // indirect
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d h1:v+kZn9ami23xBsruyZmKErIOSlCdW9pR8wfHUg5+jys=
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d/go.mod h1:6w2Pr38zOAtBn+ld/jknwI4kgMfwanCIcFVnPykdPZQ=
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d h1:8/Qhy5T6mb49KipoHnWQaG+uQ5Cjo9/tRaL8MFojG+g=
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d/go.mod h1:6w2Pr38zOAtBn+ld/jknwI4kgMfwanCIcFVnPykdPZQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
|
@ -139,6 +139,17 @@ func (s EnvironmentSettings) addWorkloadIdentityProvider(creds *[]azcore.TokenCr
|
|||
}
|
||||
}
|
||||
|
||||
func (s EnvironmentSettings) addSpiffeWorkloadIdentityProvider(creds *[]azcore.TokenCredential, errs *[]error) {
|
||||
if c, e := s.GetSpiffeWorkloadIdentity(); e == nil {
|
||||
cred, err := c.GetTokenCredential()
|
||||
if err == nil {
|
||||
*creds = append(*creds, cred)
|
||||
} else {
|
||||
*errs = append(*errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s EnvironmentSettings) addManagedIdentityProvider(timeout time.Duration, creds *[]azcore.TokenCredential, errs *[]error) {
|
||||
c := s.GetMSI()
|
||||
msiCred, err := c.GetTokenCredential()
|
||||
|
@ -172,6 +183,8 @@ func (s EnvironmentSettings) addProviderByAuthMethodName(authMethod string, cred
|
|||
s.addClientCertificateProvider(creds, errs)
|
||||
case "workloadidentity", "wi":
|
||||
s.addWorkloadIdentityProvider(creds, errs)
|
||||
case "spiffeworkloadidentity", "spiffe":
|
||||
s.addSpiffeWorkloadIdentityProvider(creds, errs)
|
||||
case "managedidentity", "mi":
|
||||
s.addManagedIdentityProvider(1*time.Second, creds, errs)
|
||||
case "commandlineinterface", "cli":
|
||||
|
@ -180,13 +193,13 @@ func (s EnvironmentSettings) addProviderByAuthMethodName(authMethod string, cred
|
|||
}
|
||||
|
||||
func getAzureAuthMethods() []string {
|
||||
return []string{"clientcredentials", "creds", "clientcertificate", "cert", "workloadidentity", "wi", "managedidentity", "mi", "commandlineinterface", "cli", "none"}
|
||||
return []string{"clientcredentials", "creds", "clientcertificate", "cert", "workloadidentity", "wi", "spiffeworkloadidentity", "spiffe", "managedidentity", "mi", "commandlineinterface", "cli", "none"}
|
||||
}
|
||||
|
||||
// GetTokenCredential returns an azcore.TokenCredential retrieved from the order specified via
|
||||
// the azureAuthMethods component metadata property which denotes a comma-separated list of auth methods to try in order.
|
||||
// The possible values contained are (case-insensitive):
|
||||
// ServicePrincipal, Certificate, WorkloadIdentity, ManagedIdentity, CLI
|
||||
// ServicePrincipal, Certificate, WorkloadIdentity, SPIFFEWorkloadIdentity, ManagedIdentity, CLI
|
||||
// The string "None" can be used to disable Azure authentication.
|
||||
//
|
||||
// If the azureAuthMethods property is not present, the following order is used (which with the exception of step 5
|
||||
|
@ -194,8 +207,9 @@ func getAzureAuthMethods() []string {
|
|||
// 1. Client credentials
|
||||
// 2. Client certificate
|
||||
// 3. Workload identity
|
||||
// 4. MSI (we use a timeout of 1 second when no compatible managed identity implementation is available)
|
||||
// 5. Azure CLI
|
||||
// 4. SPIFFE workload identity
|
||||
// 5. MSI (we use a timeout of 1 second when no compatible managed identity implementation is available)
|
||||
// 6. Azure CLI
|
||||
func (s EnvironmentSettings) GetTokenCredential() (azcore.TokenCredential, error) {
|
||||
// Create a chain
|
||||
var creds []azcore.TokenCredential
|
||||
|
@ -212,10 +226,13 @@ func (s EnvironmentSettings) GetTokenCredential() (azcore.TokenCredential, error
|
|||
// 3. Workload identity
|
||||
s.addWorkloadIdentityProvider(&creds, &errs)
|
||||
|
||||
// 4. MSI with timeout of 1 second (same as DefaultAzureCredential)
|
||||
// 4. SPIFFE workload identity
|
||||
s.addSpiffeWorkloadIdentityProvider(&creds, &errs)
|
||||
|
||||
// 5. MSI with timeout of 1 second (same as DefaultAzureCredential)
|
||||
s.addManagedIdentityProvider(1*time.Second, &creds, &errs)
|
||||
|
||||
// 5. AzureCLICredential
|
||||
// 6. AzureCLICredential
|
||||
// We omit this if running in a cloud environment
|
||||
if !isCloudServiceWithManagedIdentity() {
|
||||
s.addCLIProvider(30*time.Second, &creds, &errs)
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2025 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/spiffe/go-spiffe/v2/svid/jwtsvid"
|
||||
|
||||
spiffecontext "github.com/dapr/kit/crypto/spiffe/context"
|
||||
)
|
||||
|
||||
const (
|
||||
AzureADTokenExchangeAudience = "api://AzureADTokenExchange"
|
||||
)
|
||||
|
||||
// SpiffeWorkloadIdentityConfig provides the options to get a bearer authorizer using SPIFFE-based workload identity.
|
||||
type SpiffeWorkloadIdentityConfig struct {
|
||||
TenantID string
|
||||
ClientID string
|
||||
AzureCloud *cloud.Configuration
|
||||
}
|
||||
|
||||
// GetTokenCredential returns the azcore.TokenCredential object using a SPIFFE JWT token.
|
||||
func (c SpiffeWorkloadIdentityConfig) GetTokenCredential() (azcore.TokenCredential, error) {
|
||||
if c.TenantID == "" || c.ClientID == "" {
|
||||
return nil, errors.New("parameters clientId and tenantId must be present for SPIFFE workload identity")
|
||||
}
|
||||
|
||||
var opts *azidentity.ClientAssertionCredentialOptions
|
||||
if c.AzureCloud != nil {
|
||||
opts = &azidentity.ClientAssertionCredentialOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: *c.AzureCloud,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create a token provider function that retrieves the JWT from SPIFFE context
|
||||
tokenProvider := func(ctx context.Context) (string, error) {
|
||||
tknSource, ok := spiffecontext.JWTFrom(ctx)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("failed to get JWT SVID source from context")
|
||||
}
|
||||
jwt, err := tknSource.FetchJWTSVID(ctx, jwtsvid.Params{
|
||||
Audience: AzureADTokenExchangeAudience,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get JWT SVID: %w", err)
|
||||
}
|
||||
return jwt.Marshal(), nil
|
||||
}
|
||||
|
||||
return azidentity.NewClientAssertionCredential(c.TenantID, c.ClientID, tokenProvider, opts)
|
||||
}
|
||||
|
||||
// GetSpiffeWorkloadIdentity creates a config object from the available SPIFFE workload identity credentials.
|
||||
// An error is returned if required credentials are not available.
|
||||
func (s EnvironmentSettings) GetSpiffeWorkloadIdentity() (config SpiffeWorkloadIdentityConfig, err error) {
|
||||
azureCloud, err := s.GetAzureEnvironment()
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
|
||||
config.ClientID, _ = s.GetEnvironment("ClientID")
|
||||
config.TenantID, _ = s.GetEnvironment("TenantID")
|
||||
|
||||
if config.ClientID == "" || config.TenantID == "" {
|
||||
return config, errors.New("parameters clientId and tenantId must be present for SPIFFE workload identity")
|
||||
}
|
||||
|
||||
config.AzureCloud = azureCloud
|
||||
|
||||
return config, nil
|
||||
}
|
|
@ -161,7 +161,6 @@ func (opts *ContainerClientOpts) InitContainerClient(azEnvSettings azauth.Enviro
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot init blob storage container client with shared key: %w", err)
|
||||
}
|
||||
|
||||
// Use Azure AD as fallback
|
||||
default:
|
||||
credential, tokenErr := azEnvSettings.GetTokenCredential()
|
||||
|
|
2
go.mod
2
go.mod
|
@ -60,7 +60,7 @@ require (
|
|||
github.com/cohesion-org/deepseek-go v1.2.0
|
||||
github.com/cyphar/filepath-securejoin v0.2.4
|
||||
github.com/dancannon/gorethink v4.0.0+incompatible
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d
|
||||
github.com/didip/tollbooth/v7 v7.0.1
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3
|
||||
github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5
|
||||
|
|
4
go.sum
4
go.sum
|
@ -509,8 +509,8 @@ github.com/dancannon/gorethink v4.0.0+incompatible h1:KFV7Gha3AuqT+gr0B/eKvGhbjm
|
|||
github.com/dancannon/gorethink v4.0.0+incompatible/go.mod h1:BLvkat9KmZc1efyYwhz3WnybhRZtgF1K929FD8z1avU=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d h1:v+kZn9ami23xBsruyZmKErIOSlCdW9pR8wfHUg5+jys=
|
||||
github.com/dapr/kit v0.15.3-0.20250516121556-bc7dc566c45d/go.mod h1:6w2Pr38zOAtBn+ld/jknwI4kgMfwanCIcFVnPykdPZQ=
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d h1:8/Qhy5T6mb49KipoHnWQaG+uQ5Cjo9/tRaL8MFojG+g=
|
||||
github.com/dapr/kit v0.15.3-0.20250522135818-baea6263991d/go.mod h1:6w2Pr38zOAtBn+ld/jknwI4kgMfwanCIcFVnPykdPZQ=
|
||||
github.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
|
@ -19,6 +19,23 @@ builtinAuthenticationProfiles:
|
|||
sensitive: false
|
||||
description: "The storage account name"
|
||||
example: '"mystorageaccount"'
|
||||
- name: "azuread.spiffe"
|
||||
metadata:
|
||||
- name: accountName
|
||||
required: true
|
||||
sensitive: false
|
||||
description: "The storage account name"
|
||||
example: '"mystorageaccount"'
|
||||
- name: tenantId
|
||||
required: true
|
||||
sensitive: false
|
||||
description: "The Azure AD tenant ID"
|
||||
example: '"00000000-0000-0000-0000-000000000000"'
|
||||
- name: clientId
|
||||
required: true
|
||||
sensitive: false
|
||||
description: "The Azure AD client ID (application ID)"
|
||||
example: '"00000000-0000-0000-0000-000000000000"'
|
||||
authenticationProfiles:
|
||||
- title: "Connection string"
|
||||
description: "Authenticate using a connection string."
|
||||
|
|
Loading…
Reference in New Issue