Skip to content

Commit

Permalink
[Feature][Add] Added AWS Secret Manager support (#1)
Browse files Browse the repository at this point in the history
* Added a code for AWS Secret Manager

Signed-off-by: iamabhishek-dubey <[email protected]>

* Upgraded version

Signed-off-by: iamabhishek-dubey <[email protected]>

* Updated information for AWS Secret Manager

Signed-off-by: iamabhishek-dubey <[email protected]>
  • Loading branch information
iamabhishek-dubey authored May 8, 2021
1 parent 4d0bec0 commit 2341250
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### v2.0
##### May 8, 2021

#### :tada: [Features Added]

- Added support for AWS secret manager

### v1.0
##### April 10, 2021

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
REGISTRY ?= quay.io
REPOSITORY ?= $(REGISTRY)/opstree
ARTIFACT_NAME=k8s-secret-injector
VERSION = 1.0
VERSION = 2.0

all: build-code build-image

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ k8s-secret-injector is a tool that can connect with multiple secret managers to
The secret managers which are currently supported:-

- **[Hashicorp Vault](https://www.vaultproject.io/)**
- **[AWS Secret Manager](https://aws.amazon.com/secrets-manager/)**

There are some secret managers which are planned to be implemented in future.

- **[AWS Secret Manager](https://aws.amazon.com/secrets-manager/)**
- **[Azure Key Vault](https://azure.microsoft.com/en-in/services/key-vault/)**
- **[GCP Secret Manager](https://cloud.google.com/secret-manager)**

### Supported Features

- k8s-secret-injector can connect with Vault using Kubernetes as backend
- Authenticate with Kubernetes using Serviceaccount mechanism
- Support AWS Secret Manager as secret manager backend
- Inject secrets directly to the process, i.e. after the injection you cannot read secrets from the environment variable
- Inject secret to pod's application process from AWS Secret Manager

### Architecture

Expand Down Expand Up @@ -52,6 +54,7 @@ Usage:
k8s-secret-injector [command]

Available Commands:
aws Fetch secrets from AWS Secret Manager
help Help about any command
vault Fetch and inject secrets from Vault to a given command
version Print the version of k8s secret injector
Expand Down
56 changes: 56 additions & 0 deletions cmd/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cmd

import (
awsSDK "github.com/aws/aws-sdk-go/aws"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s-secret-injector/pkg/aws"
)

var (
region string
secretNameAWS string
previousVersion string
roleARN string
)

// awsCmd represents the aws command
var awsCmd = &cobra.Command{
Use: "aws",
Short: "Fetch secrets from AWS Secret Manager",
Long: `Fetch secrets from AWS Secret Manager`,
Run: func(cmd *cobra.Command, args []string) {
var (
secretData map[string]interface{}
err error
)

cfg := &aws.Config{
Region: region,
RoleARN: roleARN,
PreviousVersion: previousVersion,
SecretName: awsSDK.String(secretNameAWS),
}

secretData, err = aws.RetrieveSecret(cfg)
if err != nil {
exitWithError("Error getting secrets from AWS Secret manager", err)
}
processSecrets(secretData, args)
},
}

func init() {
RootCmd.AddCommand(awsCmd)

viper.SetDefault("region", "us-east-1")
viper.SetDefault("role_arn", "")
viper.SetDefault("secret_name", "")
viper.SetDefault("previous_version", "")
viper.AutomaticEnv()

awsCmd.Flags().StringVar(&region, "region", viper.GetString("region"), "AWS Region for the Secret Manager (default: us-east-1)")
awsCmd.Flags().StringVar(&roleARN, "role-arn", viper.GetString("role_arn"), "AWS Role ARN with access to the secret, this requires also permissions on the KMS key for that role")
awsCmd.Flags().StringVar(&secretNameAWS, "secret-name", viper.GetString("secret_name"), "AWS Secret Name")
awsCmd.Flags().StringVar(&previousVersion, "previous-version", viper.GetString("previous_version"), "If using lambda to rotate secrets you can get the previous version (default: current version)")
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module k8s-secret-injector
go 1.16

require (
github.com/aws/aws-sdk-go v1.30.27
github.com/fatih/color v1.9.0 // indirect
github.com/go-test/deep v1.0.7 // indirect
github.com/google/go-cmp v0.5.4 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0=
github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
Expand Down Expand Up @@ -204,6 +205,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down
87 changes: 87 additions & 0 deletions pkg/aws/secret-manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package aws

import (
"context"
"encoding/json"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
log "github.com/sirupsen/logrus"
)

// Config configuration for AWS
type Config struct {
Region string
SecretName *string
PreviousVersion string
RoleARN string
}

func newSecretManagerClient(region, roleArn string) *secretsmanager.SecretsManager {
log.Infof("Using region: %s", region)
sess := session.Must(session.NewSession(&aws.Config{
Region: aws.String(region), // Sessions Manager functions require region configuration
}))

if roleArn != "" {
log.Debugf("Using Role Arn: %s", roleArn)
// the new Credentials object wraps the AssumeRoleProvider
sess.Config.Credentials = stscreds.NewCredentials(sess, roleArn)
}

// Create a SecretsManager client with additional configuration
return secretsmanager.New(sess, aws.NewConfig().WithRegion(region))
}

// GetSecretData will fetch the secret from secret manager
func GetSecretData(api secretsmanageriface.SecretsManagerAPI, secretValueInput *secretsmanager.GetSecretValueInput) (map[string]interface{}, error) {
var secretData map[string]interface{}
ctx := context.Background()
secretValueOutput, err := api.GetSecretValueWithContext(ctx, secretValueInput)

if err != nil {
return nil, fmt.Errorf("failed to access secret version: %w", err)
}

err = json.Unmarshal([]byte(*secretValueOutput.SecretString), &secretData)
if err != nil {
return nil, fmt.Errorf("bad secret JSON data, can not decode secret JSON data: %w", err)
}
return secretData, nil
}

func buildSecretValueInput(cfg *Config) (*secretsmanager.GetSecretValueInput, error) {
secretName := cfg.SecretName
if aws.StringValue(secretName) == "" {
return nil, fmt.Errorf("error: missing SECRET_NAME environment variable")
}
versionStage := aws.String("AWSCURRENT")
if cfg.PreviousVersion != "" {
versionStage = aws.String("AWSPREVIOUS")
}
secretValueInput := &secretsmanager.GetSecretValueInput{
SecretId: secretName,
VersionStage: versionStage,
}
return secretValueInput, nil
}

// RetrieveSecret from AWS secrets manager
func RetrieveSecret(cfg *Config) (map[string]interface{}, error) {
log.Info("Using AWS Secret Manager")
secretValueInput, err := buildSecretValueInput(cfg)
if err != nil {
return nil, err
}

client := newSecretManagerClient(cfg.Region, cfg.RoleARN)
secretData, err := GetSecretData(client, secretValueInput)
if err != nil {
return nil, err
}
return secretData, nil
}
2 changes: 1 addition & 1 deletion pkg/version/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package version

var version = "1.0"
var version = "2.0"

// GetVersion will return the version of secret injector
func GetVersion() string {
Expand Down

0 comments on commit 2341250

Please sign in to comment.