package secrets_manager import ( "encoding/base64" credentials2 "github.com/aws/aws-sdk-go/aws/credentials" "os" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/logs" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/struct_utils" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-secretsmanager-caching-go/secretcache" ) type DatabaseCredentials struct { Username string `json:"username"` Password string `json:"password"` Engine string `json:"engine"` Host string `json:"host"` Port int `json:"port"` InstanceIdentifier string `json:"dbInstanceIdentifier"` ReadOnlyHost string `json:"aurora_read_only_host"` } type S3UploadCredentials struct { AccessKeyID string `json:"accessKeyID"` SecretKey string `json:"secretKey"` } var ( secretCache, _ = secretcache.New() secretManagerRegion = "af-south-1" ) func GetDatabaseCredentials(secretID string, isDebug bool) (DatabaseCredentials, error) { secret, _ := GetSecret(secretID, isDebug) var credentials DatabaseCredentials err := struct_utils.UnmarshalJSON([]byte(secret), &credentials) if err != nil { return DatabaseCredentials{}, err } return credentials, nil } func GetS3UploadCredentials(secretID string, isDebug bool) (*credentials2.Credentials, error) { secret, _ := GetSecret(secretID, isDebug) var credentials S3UploadCredentials err := struct_utils.UnmarshalJSON([]byte(secret), &credentials) if err != nil { return nil, err } return credentials2.NewStaticCredentials(credentials.AccessKeyID, credentials.SecretKey, ""), nil } func GetSecret(secretID string, isDebug bool) (string, string) { cachedSecret, err := secretCache.GetSecretString(secretID) if err != nil { logs.Info("Failed to get secret key from cache") } if cachedSecret != "" { return cachedSecret, "" } awsSession := session.New() // Get local config if isDebug && os.Getenv("ENVIRONMENT") != "" { logs.Info("Using access key %s", os.Getenv("AWS_ACCESS_KEY_ID")) awsSession, err = session.NewSessionWithOptions(session.Options{ Config: aws.Config{ Region: aws.String("af-south-1"), CredentialsChainVerboseErrors: aws.Bool(true), }, }) if err != nil { return "", "" } } // Create a Secrets Manager client svc := secretsmanager.New(awsSession, aws.NewConfig().WithRegion(secretManagerRegion)) input := &secretsmanager.GetSecretValueInput{ SecretId: aws.String(string(secretID)), VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified } // In this sample we only handle the specific exceptions for the 'GetSecretValue' API. // See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html result, err := svc.GetSecretValue(input) if err != nil { if aerr, ok := err.(awserr.Error); ok { switch aerr.Code() { case secretsmanager.ErrCodeDecryptionFailure: // Secrets Manager can't decrypt the protected secret text using the provided KMS key. logs.Info(secretsmanager.ErrCodeDecryptionFailure, aerr.Error()) case secretsmanager.ErrCodeInternalServiceError: // An error occurred on the server side. logs.Info(secretsmanager.ErrCodeInternalServiceError, aerr.Error()) case secretsmanager.ErrCodeInvalidParameterException: // You provided an invalid value for a parameter. logs.Info(secretsmanager.ErrCodeInvalidParameterException, aerr.Error()) case secretsmanager.ErrCodeInvalidRequestException: // You provided a parameter value that is not valid for the current state of the resource. logs.Info(secretsmanager.ErrCodeInvalidRequestException, aerr.Error()) case secretsmanager.ErrCodeResourceNotFoundException: // We can't find the resource that you asked for. logs.Info("Can't find secret with ID: ", secretID) logs.Info(secretsmanager.ErrCodeResourceNotFoundException, aerr.Error()) default: logs.Info(err.Error()) } } else { // Print the error, cast err to awserr.Error to get the Code and // Message from an error. logs.Info(err.Error()) } return "", "" } // Decrypts secret using the associated KMS CMK. // Depending on whether the secret is a string or binary, one of these fields will be populated. var secretString, decodedBinarySecret string if result.SecretString != nil { secretString = *result.SecretString } else { decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary))) length, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary) if err != nil { logs.Info("Base64 Decode Error:", err) return "", "" } decodedBinarySecret = string(decodedBinarySecretBytes[:length]) } return secretString, decodedBinarySecret }