mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-06 23:46:29 +02:00
152 lines
5.4 KiB
Go
152 lines
5.4 KiB
Go
|
package devops
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"log"
|
||
|
"path/filepath"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws"
|
||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||
|
"github.com/aws/aws-sdk-go/service/secretsmanager"
|
||
|
"github.com/pkg/errors"
|
||
|
"golang.org/x/crypto/acme/autocert"
|
||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||
|
)
|
||
|
|
||
|
// SecretManagerAutocertCache implements the autocert.Cache interface for AWS Secrets Manager that is used by Manager
|
||
|
// to store and retrieve previously obtained certificates and other account data as opaque blobs.
|
||
|
type SecretManagerAutocertCache struct {
|
||
|
awsSession *session.Session
|
||
|
log *log.Logger
|
||
|
secretPrefix string
|
||
|
}
|
||
|
|
||
|
// SyncCfgInit provides the functionality to keep config files sync'd between running tasks and across deployments.
|
||
|
func NewSecretManagerAutocertCache(log *log.Logger, awsSession *session.Session, secretPrefix string ) (*SecretManagerAutocertCache, error) {
|
||
|
return &SecretManagerAutocertCache{
|
||
|
awsSession,
|
||
|
log,
|
||
|
secretPrefix,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Get returns a certificate data for the specified key.
|
||
|
// If there's no such key, Get returns ErrCacheMiss.
|
||
|
func (c *SecretManagerAutocertCache) Get(ctx context.Context, key string) ([]byte, error) {
|
||
|
|
||
|
svc := secretsmanager.New(c.awsSession)
|
||
|
|
||
|
secretID := filepath.Join(c.secretPrefix, key)
|
||
|
|
||
|
// Load the secret by ID from Secrets Manager.
|
||
|
res, err := svc.GetSecretValue(&secretsmanager.GetSecretValueInput{
|
||
|
SecretId: aws.String(secretID),
|
||
|
})
|
||
|
if err != nil {
|
||
|
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == secretsmanager.ErrCodeResourceNotFoundException {
|
||
|
return nil, autocert.ErrCacheMiss
|
||
|
}
|
||
|
|
||
|
return nil, errors.Wrapf(err, "failed to get value for secret id %s", secretID)
|
||
|
}
|
||
|
|
||
|
log.Printf("AWS Secrets Manager : Secret %s found", secretID)
|
||
|
|
||
|
return res.SecretBinary, nil
|
||
|
}
|
||
|
|
||
|
// Put stores the data in the cache under the specified key.
|
||
|
// Underlying implementations may use any data storage format,
|
||
|
// as long as the reverse operation, Get, results in the original data.
|
||
|
func (c *SecretManagerAutocertCache) Put(ctx context.Context, key string, data []byte) error {
|
||
|
|
||
|
svc := secretsmanager.New(c.awsSession)
|
||
|
|
||
|
secretID := filepath.Join(c.secretPrefix, key)
|
||
|
|
||
|
// Create the new entry in AWS Secret Manager for the file.
|
||
|
_, err := svc.CreateSecret(&secretsmanager.CreateSecretInput{
|
||
|
Name: aws.String(secretID),
|
||
|
SecretString: aws.String(string(data)),
|
||
|
})
|
||
|
if err != nil {
|
||
|
if aerr, ok := err.(awserr.Error); !ok {
|
||
|
|
||
|
if aerr.Code() == secretsmanager.ErrCodeInvalidRequestException {
|
||
|
// InvalidRequestException: You can't create this secret because a secret with this
|
||
|
// name is already scheduled for deletion.
|
||
|
|
||
|
// Restore secret after it was already previously deleted.
|
||
|
_, err = svc.RestoreSecret(&secretsmanager.RestoreSecretInput{
|
||
|
SecretId: aws.String(secretID),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "autocert failed to restore secret %s", secretID)
|
||
|
}
|
||
|
|
||
|
} else if aerr.Code() != secretsmanager.ErrCodeResourceExistsException {
|
||
|
return errors.Wrapf(err, "autocert failed to create secret %s", secretID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If where was a resource exists error for create, then need to update the secret instead.
|
||
|
_, err = svc.UpdateSecret(&secretsmanager.UpdateSecretInput{
|
||
|
SecretId: aws.String(secretID),
|
||
|
SecretString: aws.String(string(data)),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "autocert failed to update secret %s", secretID)
|
||
|
}
|
||
|
|
||
|
log.Printf("AWS Secrets Manager : Secret %s updated", secretID)
|
||
|
} else {
|
||
|
log.Printf("AWS Secrets Manager : Secret %s created", secretID)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Delete removes a certificate data from the cache under the specified key.
|
||
|
// If there's no such key in the cache, Delete returns nil.
|
||
|
func (c *SecretManagerAutocertCache) Delete(ctx context.Context, key string) error {
|
||
|
|
||
|
svc := secretsmanager.New(c.awsSession)
|
||
|
|
||
|
secretID := filepath.Join(c.secretPrefix, key)
|
||
|
|
||
|
// Create the new entry in AWS Secret Manager for the file.
|
||
|
_, err := svc.DeleteSecret(&secretsmanager.DeleteSecretInput{
|
||
|
SecretId: aws.String(secretID),
|
||
|
|
||
|
// (Optional) Specifies that the secret is to be deleted without any recovery
|
||
|
// window. You can't use both this parameter and the RecoveryWindowInDays parameter
|
||
|
// in the same API call.
|
||
|
//
|
||
|
// An asynchronous background process performs the actual deletion, so there
|
||
|
// can be a short delay before the operation completes. If you write code to
|
||
|
// delete and then immediately recreate a secret with the same name, ensure
|
||
|
// that your code includes appropriate back off and retry logic.
|
||
|
//
|
||
|
// Use this parameter with caution. This parameter causes the operation to skip
|
||
|
// the normal waiting period before the permanent deletion that AWS would normally
|
||
|
// impose with the RecoveryWindowInDays parameter. If you delete a secret with
|
||
|
// the ForceDeleteWithouRecovery parameter, then you have no opportunity to
|
||
|
// recover the secret. It is permanently lost.
|
||
|
ForceDeleteWithoutRecovery: aws.Bool(false),
|
||
|
|
||
|
// (Optional) Specifies the number of days that Secrets Manager waits before
|
||
|
// it can delete the secret. You can't use both this parameter and the ForceDeleteWithoutRecovery
|
||
|
// parameter in the same API call.
|
||
|
//
|
||
|
// This value can range from 7 to 30 days.
|
||
|
RecoveryWindowInDays: aws.Int64(30),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "autocert failed to delete secret %s", secretID)
|
||
|
}
|
||
|
|
||
|
log.Printf("AWS Secrets Manager : Secret %s deleted for %s", secretID)
|
||
|
|
||
|
return nil
|
||
|
}
|