Skip to content

Commit

Permalink
Add support for aws secret manager authentication
Browse files Browse the repository at this point in the history
Signed-off-by: geoffrey1330 <israelgeoffrey13@gmail.com>
  • Loading branch information
geoffrey1330 committed Nov 7, 2023
1 parent e5f21f8 commit 7e75b9b
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 128 deletions.
7 changes: 5 additions & 2 deletions apis/keda/v1alpha1/triggerauthentication_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ type AzureKeyVaultCloudInfo struct {
// AwsSecretManager is used to authenticate using AwsSecretManager
type AwsSecretManager struct {
Credentials *AwsSecretManagerCredentials `json:"credentials"`
Secrets []AwsSecretManagerSecret `json:"secret"`
Secrets []AwsSecretManagerSecret `json:"secrets"`

// +optional
PodIdentity *AuthPodIdentity `json:"podIdentity"`
Expand All @@ -297,7 +297,10 @@ type AwsSecretManager struct {
}

type AwsSecretManagerCredentials struct {
ValuesFrom string `json:"valueFrom"`
AccessKey ValueFromSecret `json:"accessKey"`
AccessSecretKey ValueFromSecret `json:"accessSecretKey"`
// +optional
AccessToken ValueFromSecret `json:"accessToken,omitempty"`
}

type AwsSecretManagerSecret struct {
Expand Down
3 changes: 3 additions & 0 deletions apis/keda/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions config/crd/bases/keda.sh_clustertriggerauthentications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,100 @@ spec:
spec:
description: TriggerAuthenticationSpec defines the various ways to authenticate
properties:
awsSecretManager:
description: AwsSecretManager is used to authenticate using AwsSecretManager
properties:
cloud:
properties:
endpoint:
type: string
region:
type: string
type: object
credentials:
properties:
accessKey:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
accessSecretKey:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
accessToken:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
required:
- accessKey
- accessSecretKey
type: object
podIdentity:
description: AuthPodIdentity allows users to select the platform
native identity mechanism
properties:
identityId:
type: string
provider:
description: PodIdentityProvider contains the list of providers
type: string
required:
- provider
type: object
secrets:
items:
properties:
name:
type: string
parameter:
type: string
versionId:
type: string
versionStage:
type: string
required:
- name
- parameter
type: object
type: array
required:
- cloud
- credentials
- secrets
type: object
azureKeyVault:
description: AzureKeyVault is used to authenticate using Azure Key
Vault
Expand Down
94 changes: 94 additions & 0 deletions config/crd/bases/keda.sh_triggerauthentications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,100 @@ spec:
spec:
description: TriggerAuthenticationSpec defines the various ways to authenticate
properties:
awsSecretManager:
description: AwsSecretManager is used to authenticate using AwsSecretManager
properties:
cloud:
properties:
endpoint:
type: string
region:
type: string
type: object
credentials:
properties:
accessKey:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
accessSecretKey:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
accessToken:
properties:
secretKeyRef:
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
required:
- secretKeyRef
type: object
required:
- accessKey
- accessSecretKey
type: object
podIdentity:
description: AuthPodIdentity allows users to select the platform
native identity mechanism
properties:
identityId:
type: string
provider:
description: PodIdentityProvider contains the list of providers
type: string
required:
- provider
type: object
secrets:
items:
properties:
name:
type: string
parameter:
type: string
versionId:
type: string
versionStage:
type: string
required:
- name
- parameter
type: object
type: array
required:
- cloud
- credentials
- secrets
type: object
azureKeyVault:
description: AzureKeyVault is used to authenticate using Azure Key
Vault
Expand Down
80 changes: 44 additions & 36 deletions pkg/scaling/resolver/aws_secretManager_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
corev1listers "k8s.io/client-go/listers/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
)

const (
AccessKeyID = "AWS_ACCESS_KEY_ID"
SecretAccessKey = "AWS_SECRET_ACCESS_KEY"
)

type AwsSecretManagerHandler struct {
secretManager *kedav1alpha1.AwsSecretManager
session *session.Session
secretclient *secretsmanager.SecretsManager
}

Expand All @@ -34,11 +33,14 @@ func NewAwsSecretManagerHandler(a *kedav1alpha1.AwsSecretManager) *AwsSecretMana

func (ash *AwsSecretManagerHandler) Read(secretName, versionID, versionStage string) (string, error) {
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String(secretName),
VersionId: aws.String(versionID),
VersionStage: aws.String(versionStage),
SecretId: aws.String(secretName),
}
if versionID != "" {
input.VersionId = aws.String(versionID)
}
if versionStage != "" {
input.VersionStage = aws.String(versionStage)
}

result, err := ash.secretclient.GetSecretValue(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
Expand All @@ -63,28 +65,30 @@ func (ash *AwsSecretManagerHandler) Read(secretName, versionID, versionStage str
return "", err
}
} else {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
err = fmt.Errorf(err.Error())
return "", err
}
}
return *result.SecretString, nil
}

func (ash *AwsSecretManagerHandler) Initialize(ctx context.Context, client client.Client, logger logr.Logger, triggerNamespace string, secretsLister corev1listers.SecretLister) error {
config, err := ash.getconfig(ctx, client, logger, triggerNamespace, secretsLister)
func (ash *AwsSecretManagerHandler) Initialize(ctx context.Context, client client.Client, logger logr.Logger, triggerNamespace string, secretsLister corev1listers.SecretLister, podTemplateSpec *corev1.PodTemplateSpec) error {
config, err := ash.getcredentials(ctx, client, logger, triggerNamespace, secretsLister, podTemplateSpec)
if err != nil {
return err
}

sess, err := session.NewSession()

ash.secretclient = secretsmanager.New(sess, config)
if ash.secretManager.Cloud.Region != "" {
config.WithRegion(ash.secretManager.Cloud.Region)
}
if ash.secretManager.Cloud.Endpoint != "" {
config.WithEndpoint(ash.secretManager.Cloud.Endpoint)
}
ash.session = session.Must(session.NewSession())
ash.secretclient = secretsmanager.New(ash.session, config)
return err
}

func (ash *AwsSecretManagerHandler) getconfig(ctx context.Context, client client.Client, logger logr.Logger, triggerNamespace string, secretsLister corev1listers.SecretLister) (*aws.Config, error) {
func (ash *AwsSecretManagerHandler) getcredentials(ctx context.Context, client client.Client, logger logr.Logger, triggerNamespace string, secretsLister corev1listers.SecretLister, podTemplateSpec *corev1.PodTemplateSpec) (*aws.Config, error) {
config := aws.NewConfig()

podIdentity := ash.secretManager.PodIdentity
Expand All @@ -94,32 +98,36 @@ func (ash *AwsSecretManagerHandler) getconfig(ctx context.Context, client client

switch podIdentity.Provider {
case "", kedav1alpha1.PodIdentityProviderNone:
secretName := ash.secretManager.Credentials.ValuesFrom
accessKeyID := resolveAuthSecret(ctx, client, logger, secretName, triggerNamespace, AccessKeyID, secretsLister)
accessSecretKey := resolveAuthSecret(ctx, client, logger, secretName, triggerNamespace, SecretAccessKey, secretsLister)
accessKeyID := resolveAuthSecret(ctx, client, logger, ash.secretManager.Credentials.AccessKey.SecretKeyRef.Name, triggerNamespace, ash.secretManager.Credentials.AccessKey.SecretKeyRef.Key, secretsLister)
accessSecretKey := resolveAuthSecret(ctx, client, logger, ash.secretManager.Credentials.AccessSecretKey.SecretKeyRef.Name, triggerNamespace, ash.secretManager.Credentials.AccessSecretKey.SecretKeyRef.Key, secretsLister)
if accessKeyID == "" || accessSecretKey == "" {
return nil, fmt.Errorf("%s and %s are expected when not using a pod identity provider", AccessKeyID, SecretAccessKey)
return nil, fmt.Errorf("AccessKeyId and AccessSecretKey are expected when not using a pod identity provider")
}
config.WithCredentials(credentials.NewStaticCredentials(accessKeyID, accessSecretKey, ""))
if ash.secretManager.Cloud.Region != "" {
config.WithRegion(ash.secretManager.Cloud.Region)
}
if ash.secretManager.Cloud.Endpoint != "" {
config.WithEndpoint(ash.secretManager.Cloud.Endpoint)
}
return config, nil

case kedav1alpha1.PodIdentityProviderAwsKiam, kedav1alpha1.PodIdentityProviderAwsEKS:
if ash.secretManager.Cloud.Region != "" {
config.WithRegion(ash.secretManager.Cloud.Region)
}
if ash.secretManager.Cloud.Endpoint != "" {
config.WithEndpoint(ash.secretManager.Cloud.Endpoint)
case kedav1alpha1.PodIdentityProviderAwsEKS:
awsRoleArn, err := ash.getRoleArnAwsEKS(ctx, client, logger, triggerNamespace, podTemplateSpec)
if err != nil {
return nil, fmt.Errorf("error resolving role arn for AwsEKS pod identity: %s", err)
}

config.WithCredentials(stscreds.NewCredentials(ash.session, awsRoleArn))
return config, nil
case kedav1alpha1.PodIdentityProviderAwsKiam:
awsRoleArn := podTemplateSpec.ObjectMeta.Annotations[kedav1alpha1.PodIdentityAnnotationKiam]
config.WithCredentials(stscreds.NewCredentials(ash.session, awsRoleArn))
return config, nil

default:
return nil, fmt.Errorf("pod identity provider %s not supported", podIdentity.Provider)
}
}

func (ash *AwsSecretManagerHandler) getRoleArnAwsEKS(ctx context.Context, client client.Client, _ logr.Logger, triggerNamespace string, podTemplateSpec *corev1.PodTemplateSpec) (string, error) {
serviceAccountName := podTemplateSpec.Spec.ServiceAccountName
serviceAccount := &corev1.ServiceAccount{}
err := client.Get(ctx, types.NamespacedName{Name: serviceAccountName, Namespace: triggerNamespace}, serviceAccount)
if err != nil {
return "", err
}
return serviceAccount.Annotations[kedav1alpha1.PodIdentityAnnotationEKS], nil
}

0 comments on commit 7e75b9b

Please sign in to comment.