keda icon indicating copy to clipboard operation
keda copied to clipboard

Provide support for using Azure AD service principal authentication with client secret/certificate

Open Aashish93-stack opened this issue 2 years ago • 22 comments

Proposal

Proposing the following updates:

  • Add a provider for azure service principal
  • Update the AuthPodIdentity, triggerAuthentication to take in a ClientID, Audience and Secret
  • Adding mechanism to load the base64 decoded Kubernetes secret
  • Adding a mechanism to decode the certificate and get the AadToken

Use-Case

We have a use case where we need to support System Assigned Identity for accessing eventhub and since PodIdentity and Workload Identities don't support System-Assigned Identity out of box, we are looking into service-principal based authentication using certificates

Anything else?

No response

Aashish93-stack avatar Nov 29 '22 19:11 Aashish93-stack

I'd be happy to contribute this feature to the project.

Aashish93-stack avatar Nov 29 '22 19:11 Aashish93-stack

Hi @Aashish93-stack You can use Workload Identities with the service principal, doesn't it solve your problem? I mean, if you already have a service principal, you can federate it with the cluster and use Workload Identity using that service principal without any secret/certificate

JorTurFer avatar Nov 29 '22 20:11 JorTurFer

@JorTurFer unfortunately, there are some use cases where we don't have access to the service principal to configure federation and other cases where users don't won't to onboard the workload-identity, so we are hoping to add a certificate based authentication to cover all our bases

Aashish93-stack avatar Nov 30 '22 18:11 Aashish93-stack

Yes, this is another way of authenticating.

I think service principal support makes sense but I'd suggest to post a configuration proposal here first to make sure we are all aligned first.

Is that OK for you @Aashish93-stack?

tomkerkhove avatar Dec 02 '22 08:12 tomkerkhove

Agreed with @tomkerkhove

BTW, There is also the option of using User Managed Identities if you need them as WI already supports them

JorTurFer avatar Dec 02 '22 09:12 JorTurFer

BUt then you still need Workload Identity :) In some scenarios you just want to use a SP.

tomkerkhove avatar Dec 02 '22 10:12 tomkerkhove

@tomkerkhove sure that works for me.

Under the triggerAunthentication I was thinking of adding a provider azure-service-principal and it would have the following structure

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: {trigger-authentication-name}
  namespace: {trigger-authentication-namespace}
spec:
  podIdentity:
     provider: azure-service-principal
     clientId: {{UUID}}
     tenantId: {{UUID}}
     audience: {{audience}}
 secretTargetRef:               #Optional: Required when using azureServicePrincipal and not Secret from Azure KeyVault
    - parameter: {{secretRef}}
      name: {{secret-name}}
      key: {secret-key-name}
 azureKeyVault:                #Optional: Required when using azureServicePrincipal and not referencing Secret from Kubernetes                 
      vaultURI: {key-vault-address}                                        
      credentials:                                                          
          clientId: {azure-ad-client-id}                                      
          clientSecret:                                                       
             valueFrom:                                                        
                secretKeyRef:                                                   
                   name: {k8s-secret-with-azure-ad-secret}                       
                   key: {key-within-the-secret}                                  
          tenantId: {azure-ad-tenant-id}   

I was thinking that the certificate can imported from a Kubernetes secret or Azure Key Vault, so when specifying the provider as azureServicePrincipal we would also need either the secretTargetRef or azureKeyVault.

Then in the triggerauthentication_types add a new const

const (
	PodIdentityProviderAzureServicePrincipal PodIdentityProvider = "azure-service-principal" // New const for service-principal
	PodIdentityProviderNone                          PodIdentityProvider = "none"
	PodIdentityProviderAzure                          PodIdentityProvider = "azure"
	PodIdentityProviderAzureWorkload          PodIdentityProvider = "azure-workload"
	PodIdentityProviderGCP                            PodIdentityProvider = "gcp"
	PodIdentityProviderSpiffe                         PodIdentityProvider = "spiffe"
	PodIdentityProviderAwsEKS                      PodIdentityProvider = "aws-eks"
	PodIdentityProviderAwsKiam                    PodIdentityProvider = "aws-kiam"
)

and update the AuthPodIdentity struct with new optional fields

type AuthPodIdentity struct {
	Provider PodIdentityProvider `json:"provider"`
	// +optional
	IdentityID string `json:"identityId"`
	// +optional
	ClientID string `json:"clientId"`
	// +optional
	Audience string `json:"audience"`
	// +optional
	TenantID string `json:"tenantId"`
}

Also, we need to create a new class (could be name azure_aad_service_principal.go) which would be responsible to for decoding the certificate and getting the servicePrincipalToken, this would be called from the individual scalers. In case of EventHubScaler, after getting the servicePrincipalToken, a new JWTProvider needs to be configured and passed in the while creating a new eventhub client

Aashish93-stack avatar Dec 02 '22 19:12 Aashish93-stack

I wouldn't add this as a new pod identity because it isn't. Maybe we could add another section like azureServicePrincipal or something like that. WDYT?

JorTurFer avatar Dec 05 '22 14:12 JorTurFer

Yes, that makes sense, updating trigger auth structure to this ->

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: {trigger-authentication-name}
  namespace: {trigger-authentication-namespace}
spec:
  azureServicePrincipal:
     clientId: {{UUID}}
     tenantId: {{UUID}}
     audience: {{audience}}
 secretTargetRef:               #Optional: Required when using azureServicePrincipal and not Secret from Azure KeyVault
    - parameter: {{secretRef}}
      name: {{secret-name}}
      key: {secret-key-name}
 azureKeyVault:                #Optional: Required when using azureServicePrincipal and not referencing Secret from Kubernetes                 
      vaultURI: {key-vault-address}                                        
      credentials:                                                          
          clientId: {azure-ad-client-id}                                      
          clientSecret:                                                       
             valueFrom:                                                        
                secretKeyRef:                                                   
                   name: {k8s-secret-with-azure-ad-secret}                       
                   key: {key-within-the-secret}                                  
          tenantId: {azure-ad-tenant-id}   

and will add a new struct in the triggerauthentication_types

type AzureServicePrincipal struct {
	ClientID string `json:"clientId"`
	// +optional
	TenantID string `json:"tenantId"`
	// +optional
	Audience string `json:"audience"`
}

Since it's no longer part of the PodIdentity, was thinking of adding a AzureServicePrincipalHandler which can be used for decoding the certificate and returning the ServicePrincipalToken and this can be called by the new if block for triggerAuthSpec.AzureServicePrincipal in resolveAuthRef function under scale_resolvers.go and store the token in the authParams map, which can then be used by the individual scalers. Does this look good ?

Aashish93-stack avatar Dec 05 '22 18:12 Aashish93-stack

WDYT @v-shenoy ?

JorTurFer avatar Dec 05 '22 18:12 JorTurFer

WDYT @v-shenoy ?

Yeah, sounds good. Are we planning to add this only for Event Hub, or all Azure scalers @JorTurFer?

v-shenoy avatar Dec 06 '22 07:12 v-shenoy

I guess that this is interesting for all azure scalers, isn't?

JorTurFer avatar Dec 06 '22 07:12 JorTurFer

I guess that this is interesting for all azure scalers, isn't?

It is. I asked because the issue is titled for Event Hub.

v-shenoy avatar Dec 06 '22 07:12 v-shenoy

@Aashish93-stack , are you willing to contribute with other scalers too?

JorTurFer avatar Dec 06 '22 07:12 JorTurFer

We should do it, I don't think this is even a question :) However, we can do it in phases if @Aashish93-stack does not have the bandwidth to update all of them.

My feedback:

  • azureServicePrincipal is a bit vague imo, I would rename it to azureActiveDirectoryIdentity/azureActiveDirectoryApplication/azureActiveDirectoryServicePrincipal
  • I'd like to support both certificate and client secret which could be coming from kubernetes secret or file. I don't think Azure Key Vault should be in the initial scope because we'd need to know how to authenticate to that then etc while you could argue that we support KV as a 1P provider and they should use that maybe? I don't know; I think it might be a bit much for initial scenario
  • I'd suggest to rename clientId to align with Azure AD's terminology which means appId or applicationId would be a better fit. A lot of end-users in Azure space always get confused with clientId.

tomkerkhove avatar Dec 06 '22 08:12 tomkerkhove

@tomkerkhove cool, the suggestions look good to me, will update it to azureActiveDirectoryServicePrincipal, support both certificate and client secret and update clientId to applicationId. For keyvault I was thinking as a 1P provider, but yeah will start out with Kubernetes secret and we can look into keyvault integration later since we may want to support 3p as well.

@JorTurFer Unfortunately, don't have much bandwidth, will try to get as many scalers I can or can do it in phases

Aashish93-stack avatar Dec 06 '22 18:12 Aashish93-stack

If you are willing to contribute, obviously is dependent on your availability, doing it in phases is awesome. Also, you are not forced to do in on every scaler, other contributors can take them (as you prefer 😄 )

JorTurFer avatar Dec 06 '22 21:12 JorTurFer

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Feb 04 '23 22:02 stale[bot]

Sorry been busy with other commitments, will contribute this feature in March.

Aashish93-stack avatar Feb 04 '23 22:02 Aashish93-stack

No worries!

tomkerkhove avatar Feb 06 '23 09:02 tomkerkhove

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Apr 07 '23 09:04 stale[bot]

@Aashish93-stack are you still interested? I would possibly try to implement

radekfojtik avatar Nov 24 '23 11:11 radekfojtik