Skip to content
Snippets Groups Projects
Select Git revision
  • afc4645d0aaa207256fba25f9c5fe261f693d307
  • main default protected
  • v1.298.0
  • v1.297.0
  • v1.296.0
  • v1.295.0
  • v1.294.0
  • v1.293.0
  • v1.292.0
  • v1.291.0
  • v1.290.0
  • v1.289.0
  • v1.288.0
  • v1.287.0
  • v1.286.0
  • v1.285.0
  • v1.284.0
  • v1.283.0
  • v1.282.0
  • v1.281.0
  • v1.280.0
  • v1.279.0
22 results

secrets_manager.go

Blame
  • secrets_manager.go 5.78 KiB
    package secrets_manager
    
    import (
    	"encoding/base64"
    	"encoding/json"
    	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"
    )
    
    var secretManagerSession *secretsmanager.SecretsManager
    
    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
    }
    
    // getSecretManagerSession Instantiates a new Secrets Manager client session
    func getSecretManagerSession(isDebug bool) (err error) {
    	if secretManagerSession != nil {
    		return nil
    	}
    
    	logs.Info("Creating a new Secrets Manager session")
    	awsSession, err := session.NewSession()
    	if err != nil {
    		return err
    	}
    
    	// Get local config
    	if isDebug && os.Getenv("ENVIRONMENT") != "" {
    		awsAccessKey := os.Getenv("AWS_ACCESS_KEY_ID")
    		if len(awsAccessKey) > 0 {
    			logs.Info("Using access key %s", awsAccessKey)
    		}
    		awsSession, err = session.NewSessionWithOptions(session.Options{
    			Config: aws.Config{
    				Region:                        aws.String("af-south-1"),
    				CredentialsChainVerboseErrors: aws.Bool(true),
    			},
    		})
    		if err != nil {
    			return err
    		}
    	}
    
    	// Create a Secrets Manager client session
    	secretManagerSession = secretsmanager.New(awsSession, aws.NewConfig().WithRegion(secretManagerRegion))
    
    	return nil
    }
    
    // logError Logs any errors returned by the Secrets Manager client
    func logError(err error) {
    	if aerr, ok := err.(awserr.Error); ok {
    		logs.Info(aerr.Code()+" %s", aerr.Error())
    	} else {
    		// Print the error, cast err to awserr.Error to get the Code and
    		// Message from an error.
    		logs.Info(err.Error())
    	}
    }
    
    func GetSecret(secretID string, isDebug bool) (string, string) {
    	// Check if we have the secret in cache
    	cachedSecret, err := secretCache.GetSecretString(secretID)
    	if err != nil {
    		logs.Info("Failed to get secret key from cache")
    	}
    	if cachedSecret != "" {
    		return cachedSecret, ""
    	}
    
    	// Create a Secrets Manager client
    	err = getSecretManagerSession(isDebug)
    	if err != nil {
    		logs.Info("Could not create client: %+v", err)
    		return "", ""
    	}
    
    	// Create a secret
    	input := &secretsmanager.GetSecretValueInput{
    		SecretId:     aws.String(string(secretID)),
    		VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified
    	}
    
    	result, err := secretManagerSession.GetSecretValue(input)
    	if err != nil {
    		logError(err)
    		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: %+v", err)
    			return "", ""
    		}
    		decodedBinarySecret = string(decodedBinarySecretBytes[:length])
    	}
    
    	return secretString, decodedBinarySecret
    }
    
    // CreateSecret Creates a JSON marshaled "string secret" (can be expanded to cater for binary secrets should the need arise)
    func CreateSecret(secretID string, secret any, isDebug bool) (awsSecretID string, err error) {
    	// Create a Secrets Manager client
    	err = getSecretManagerSession(isDebug)
    	if err != nil {
    		logs.Info("Could not create client: %+v", err)
    		return "", err
    	}
    
    	// Create the secret - marshaling "any" into a JSON string
    	secretStr, err := json.Marshal(secret)
    	if err != nil {
    		logs.Info("Could not marshal secret: %+v", err)
    		return "", err
    	}
    	input := &secretsmanager.CreateSecretInput{
    		Name:         aws.String(secretID),
    		SecretString: aws.String(string(secretStr)),
    	}
    
    	result, err := secretManagerSession.CreateSecret(input)
    	if err != nil {
    		logError(err)
    		return "", err
    	}
    
    	return aws.StringValue(result.Name), nil
    }
    
    func DeleteSecret(secretID string, forceWithoutRecovery bool, isDebug bool) error {
    	// Create a Secrets Manager client
    	err := getSecretManagerSession(isDebug)
    	if err != nil {
    		logs.Info("Could not create client: %+v", err)
    		return err
    	}
    
    	// Delete the secret
    	input := &secretsmanager.DeleteSecretInput{
    		SecretId:                   aws.String(secretID),
    		ForceDeleteWithoutRecovery: aws.Bool(forceWithoutRecovery),
    	}
    
    	_, err = secretManagerSession.DeleteSecret(input)
    	if err != nil {
    		logError(err)
    		return err
    	}
    
    	return nil
    }