Skip to content

Commit

Permalink
add aws secrets manager
Browse files Browse the repository at this point in the history
  • Loading branch information
ssoroka committed Oct 22, 2021
1 parent 8ff5417 commit c3ded67
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 16 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.2
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46
github.com/aws/aws-sdk-go v1.41.8
github.com/aws/aws-sdk-go-v2/credentials v1.4.3 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cli/browser v1.1.0
github.com/docker/docker v20.10.9+incompatible
Expand Down
20 changes: 12 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -203,30 +203,34 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.41.7 h1:vlpR8Cky3ZxUVNINgeRZS6N0p6zmFvu/ZqRRwrTI25U=
github.com/aws/aws-sdk-go v1.41.7/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.41.8 h1:j6imzwVyWQYuQxbkPmg2MdMmLB+Zw+U3Ewi59YF8Rwk=
github.com/aws/aws-sdk-go v1.41.8/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.0 h1:+S+dSqQCN3MSU5vJRu1HqHrq00cJn6heIMU7X9hcsoo=
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.9.2 h1:dUFQcMNZMLON4BOe273pl0filK9RqyQMhCK/6xssL6s=
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/config v1.7.0 h1:J2cZ7qe+3IpqBEXnHUrFrOjoB9BlsXg7j53vxcl5IVg=
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
github.com/aws/aws-sdk-go-v2/credentials v1.4.0 h1:kmvesfjY861FzlCU9mvAfe01D9aeXcG2ZuC+k9F2YLM=
github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 h1:OxTAgH8Y4BXHD6PGCJ8DHx2kaZPCQfSTqmDsdRZFezE=
github.com/aws/aws-sdk-go-v2/credentials v1.4.3 h1:LTdD5QhK073MpElh9umLLP97wxphkgVC/OjQaEbBwZA=
github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0 h1:9tfxW/icbSu98C2pcNynm5jmDwU3/741F11688B6QnU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 h1:d95cddM3yTm4qffj3P6EnP+TzX1SSkWaQypXSgT/hpA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 h1:VNJ5NLBteVXEwE2F1zEXVmyIH58mZ6kIQGJoC7C+vkg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2 h1:r7jel2aa4d9Duys7wEmWqDd5ebpC9w6Kxu6wIjjp18E=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=
github.com/aws/aws-sdk-go-v2/service/kms v1.5.0 h1:10e9mzaaYIIePEuxUzW5YJ8LKHNG/NX63evcvS3ux9U=
github.com/aws/aws-sdk-go-v2/service/kms v1.5.0/go.mod h1:w7JuP9Oq1IKMFQPkNe3V6s9rOssXzOVEMNEqK1L1bao=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.6.0/go.mod h1:B+7C5UKdVq1ylkI/A6O8wcurFtaux0R1njePNPtKwoA=
github.com/aws/aws-sdk-go-v2/service/ssm v1.10.0/go.mod h1:4dXS5YNqI3SNbetQ7X7vfsMlX6ZnboJA2dulBwJx7+g=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 h1:sHXMIKYS6YiLPzmKSvDpPmOpJDHxmAUgbiF49YNVztg=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 h1:1at4e5P+lvHNl2nUktdM2/v+rpICg/QSEr9TO/uW9vU=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.2 h1:pZwkxZbspdqRGzddDB92bkZBoB7lg85sMRE7OqdB3V0=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2 h1:ol2Y5DWqnJeKqNd8th7JWzBtqu63xpOfs1Is+n1t8/4=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
Expand Down
77 changes: 77 additions & 0 deletions secrets/awssecretsmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package secrets

import (
"context"
"fmt"
"strings"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/secretsmanager"
)

var _ SecretStorage = &AWSSecretsManager{}

type AWSSecretsManager struct {
UseSecretMaps bool // TODO: support storing to json maps if this is enabled.

client *secretsmanager.SecretsManager
}

func NewAWSSecretsManager(client *secretsmanager.SecretsManager) *AWSSecretsManager {
return &AWSSecretsManager{
client: client,
}
}

// SetSecret
// must have the secretsmanager:CreateSecret permission
// if using tags, must have secretsmanager:TagResource
// if using kms customer-managed keys, also need:
// - kms:GenerateDataKey
// - kms:Decrypt
func (s *AWSSecretsManager) SetSecret(name string, secret []byte) error {
name = strings.ReplaceAll(name, ":", "_")
_, err := s.client.CreateSecretWithContext(context.TODO(), &secretsmanager.CreateSecretInput{
Name: &name,
SecretBinary: secret,
})
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case secretsmanager.ErrCodeResourceExistsException:
// try replacing instead
_, err = s.client.UpdateSecretWithContext(context.TODO(), &secretsmanager.UpdateSecretInput{
SecretBinary: secret,
SecretId: &name,
})
if err != nil {
return fmt.Errorf("update secret: %w", err)
}
return nil
}
}

return fmt.Errorf("creating secret: %w", err)
}
return nil
}

// GetSecret
// must have permission secretsmanager:GetSecretValue
// kms:Decrypt - required only if you use a customer-managed Amazon Web Services KMS key to encrypt the secret
func (s *AWSSecretsManager) GetSecret(name string) (secret []byte, err error) {
name = strings.ReplaceAll(name, ":", "_")
sec, err := s.client.GetSecretValueWithContext(context.TODO(), &secretsmanager.GetSecretValueInput{
SecretId: &name,
})
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case secretsmanager.ErrCodeResourceNotFoundException:
return nil, nil
}
}
return nil, fmt.Errorf("get secret: %w", err)
}
return sec.SecretBinary, nil
}
36 changes: 36 additions & 0 deletions secrets/awssecretsmanager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package secrets

import (
"context"
"io/ioutil"
"net/http"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/service/secretsmanager"
)

func waitForSecretsManagerReady(t *testing.T, ssm *secretsmanager.SecretsManager) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
for {
resp, err := http.Get(ssm.Client.Endpoint)
// server responds with 404 and body of status running 😂
if err == nil {
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err == nil {
if strings.Contains(string(b), "running") {
return // ready!
}
}
}
if ctx.Err() != nil {
t.Error("timeout waiting for secrets manager to be ready")
t.FailNow()
return
}
time.Sleep(100 * time.Millisecond)
}
}
54 changes: 46 additions & 8 deletions secrets/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/hashicorp/vault/api"
"github.com/infrahq/infra/testutil/docker"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -41,16 +42,31 @@ func TestMain(m *testing.M) {

var (
awskms *kms.KMS
awsssm *secretsmanager.SecretsManager
containerIDs []string
)

func setup() {
if testing.Short() {
return
}
var containerID string

// setup localstack
// eg docker run --rm -it -p 4566:4566 -p 4571:4571 localstack/localstack
containerID = docker.LaunchContainer("localstack/localstack",
[]docker.ExposedPort{
{HostPort: 4566, ContainerPort: 4566},
},
nil, // cmd
[]string{
"SERVICES=secretsmanager",
},
)
containerIDs = append(containerIDs, containerID)

// setup kms
containerID := docker.LaunchContainer("nsmithuk/local-kms", []docker.ExposedPort{
containerID = docker.LaunchContainer("nsmithuk/local-kms", []docker.ExposedPort{
{HostPort: 8380, ContainerPort: 8080},
}, nil, nil)
containerIDs = append(containerIDs, containerID)
Expand All @@ -70,11 +86,26 @@ func setup() {
containerIDs = append(containerIDs, containerID)

// configure aws client
cfg := aws.NewConfig()
cfg.Endpoint = aws.String("http://localhost:8380")
cfg.Credentials = credentials.AnonymousCredentials
cfg.Region = aws.String("us-west-2")
awskms = kms.New(session.Must(session.NewSession()), cfg)
sess := session.Must(session.NewSession())

// for kms service
cfg := aws.NewConfig().
WithEndpoint("http://localhost:8380").
WithCredentials(credentials.AnonymousCredentials).
WithRegion("us-west-2")
awskms = kms.New(sess, cfg)

// for localstack (secrets manager, etc)
cfg2 := aws.NewConfig().
WithCredentials(credentials.NewCredentials(&credentials.StaticProvider{
Value: credentials.Value{
AccessKeyID: "test",
SecretAccessKey: "test",
},
})).
WithEndpoint("http://localhost:4566").
WithRegion("us-east-1")
awsssm = secretsmanager.New(sess, cfg2)
}

func teardown() {
Expand Down Expand Up @@ -106,11 +137,18 @@ func eachSecretSymmetricKeyProvider(t *testing.T, eachFunc func(t *testing.T, p
func eachProvider(t *testing.T, eachFunc func(t *testing.T, p interface{})) {
providers := map[string]interface{}{}

// add aws
// add AWS KMS
k, err := NewAWSKMSSecretProvider(awskms)
require.NoError(t, err)

providers["kms"] = k
providers["awskms"] = k

// add AWS Secrets Manager
ssm := NewAWSSecretsManager(awsssm)

waitForSecretsManagerReady(t, awsssm)

providers["awsssm"] = ssm

// add vault
v, err := NewVaultSecretProvider("http://localhost:8200", "root", "")
Expand Down

0 comments on commit c3ded67

Please sign in to comment.