Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
martinhaus committed Mar 2, 2020
0 parents commit 600e87f
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 0 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# AWS Secrets Manager Loader

Simple go application to load secrets from AWS Secrets Manager and output them to stdout

## Build

```
go build -o aws_sm_loader main.go
```

## Usage

The application expects the env variable `AWS_REGION` to be set.
To filter the secrets you want to retrieve use AWS tags. Set tags as env variables before running the application with prefix `SM_TAG_`.

To get all secrets tagged with FOO=bar use
```
export SM_TAG_FOO=bar
./aws_sm_loader
```

The secrets matching **all** tags will be printed to stdout in the following format
```
export FOO=bar
export FOO2=bar2
...
```

You can the use eval to export the env variables, for example in dumb-init entrypoint
```
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["bash", "-c", "eval $(./aws_sm_loader) && exec printenv"]
```



191 changes: 191 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package main

// Use this code snippet in your app.
// If you need more information about configurations or implementing the sample code, visit the AWS docs:
// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/setting-up.html

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"os"
"strings"

"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"
)

var (
region string
)

type Secret struct {
Key string
Value string
}

func getSecret(secretName string) *string {

//Create a Secrets Manager client
svc := secretsmanager.New(session.New(),
aws.NewConfig().WithRegion(region))
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String(secretName),
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.
fmt.Println(secretsmanager.ErrCodeDecryptionFailure, aerr.Error())

case secretsmanager.ErrCodeInternalServiceError:
// An error occurred on the server side.
fmt.Println(secretsmanager.ErrCodeInternalServiceError, aerr.Error())

case secretsmanager.ErrCodeInvalidParameterException:
// You provided an invalid value for a parameter.
fmt.Println(secretsmanager.ErrCodeInvalidParameterException, aerr.Error())

case secretsmanager.ErrCodeInvalidRequestException:
// You provided a parameter value that is not valid for the current state of the resource.
fmt.Println(secretsmanager.ErrCodeInvalidRequestException, aerr.Error())

case secretsmanager.ErrCodeResourceNotFoundException:
// We can't find the resource that you asked for.
fmt.Println(secretsmanager.ErrCodeResourceNotFoundException, aerr.Error())
}
} else {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
}
fmt.Println(err.Error())
return nil
}

// 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 decodedBinarySecret string
if result.SecretString != nil {
return result.SecretString
} else {
decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary)))
len, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary)
if err != nil {
fmt.Println("Base64 Decode Error:", err)
return nil
}
decodedBinarySecret = string(decodedBinarySecretBytes[:len])
return &decodedBinarySecret
}
}

func listAllSecrets() *secretsmanager.ListSecretsOutput {
svc := secretsmanager.New(session.New(),
aws.NewConfig().WithRegion(region))
input := &secretsmanager.ListSecretsInput{}

result, err := svc.ListSecrets(input)
if err != nil {
fmt.Println(err.Error())
}
return result
}

func filterSecrets(targetTags map[string]string) []string {
allSecrets := listAllSecrets()
var filteredSecrets []string
for _, secret := range allSecrets.SecretList {

// If secret has no tags, skip it
if len(secret.Tags) == 0 {
continue
}

// Convert tags on resource into map
resourceTags := make(map[string]string)
for _, tag := range secret.Tags {
resourceTags[*tag.Key] = *tag.Value
}

// Check if resource has all required tags specified in env
hasAllTags := true
for key, value := range targetTags {
if resourceTags[key] != value {
hasAllTags = false
break
}
}

if hasAllTags {
filteredSecrets = append(filteredSecrets, *secret.Name)
}
}

return filteredSecrets
}

func filterEnvVars(targetPrefix string) map[string]string {

var result map[string]string

allVars := os.Environ()
result = make(map[string]string)
for _, env := range allVars {

if strings.HasPrefix(env, targetPrefix) {
trimmed := strings.TrimPrefix(env, targetPrefix)
pair := strings.SplitN(trimmed, "=", 2)
result[pair[0]] = pair[1]
}
}

return result
}

func parseSecrets(secretsNames []string) []string {
var secrets []string

for _, secret := range secretsNames {

var parsedSecret map[string]string
err := json.Unmarshal([]byte(*getSecret(secret)), &parsedSecret)
if err != nil {
fmt.Println(err)
}

for key, value := range parsedSecret {
secrets = append(secrets, "export "+key+"='"+value+"'")
}
}

return secrets
}

func main() {

region = os.Getenv("AWS_REGION")
sm_tags := filterEnvVars("SM_TAG_")

if len(sm_tags) == 0 {
err := errors.New("No tags for secrets filtering specified")
panic(err)
}

filteredSecretsNames := filterSecrets(sm_tags)
parsedSecrets := parseSecrets(filteredSecretsNames)

for _, s := range parsedSecrets {
fmt.Println(s)
}
}

0 comments on commit 600e87f

Please sign in to comment.