aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2022-11-21 00:00:55 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2022-11-21 00:01:06 -0500
commit35f9d0a511653604764dd8a033ac9cba00248443 (patch)
tree9fc3d12a4d7a48abb1a25aefbb0a36181534e4a9
parent15f0d12bf1475b5c77121abd2c0f6d0a06791dc2 (diff)
downloaddpw-ssm-master.tar.gz
dpw-ssm-master.tar.xz
Update the docs, mild reorgHEADmaster
-rwxr-xr-xbuild.sh2
-rw-r--r--main.go124
-rw-r--r--readme.md5
-rw-r--r--store/store.go37
4 files changed, 92 insertions, 76 deletions
diff --git a/build.sh b/build.sh
index e6ef70e..8e0e50b 100755
--- a/build.sh
+++ b/build.sh
@@ -8,6 +8,8 @@ $(go version)
Build Date: $(date +%m.%d.%Y)
Source code can be found here:
https://git.riedstra.dev/go/dpw-ssm
+Source code for the dynamic password manager dpw can be found here:
+https://git.riedstra.dev/mitch/dpw
$LICENSE"
diff --git a/main.go b/main.go
index 4e11d21..96323a5 100644
--- a/main.go
+++ b/main.go
@@ -1,3 +1,5 @@
+// Very basic program to interact with the AWS SSM Parameter Store
+// to let you use it as a basic key/value store for arbitrary data.
package main
import (
@@ -5,7 +7,7 @@ import (
"fmt"
"log"
"os"
- "regexp"
+ "sort"
"strings"
"riedstra.dev/go/dpw-ssm/store"
@@ -15,91 +17,78 @@ import (
"github.com/aws/aws-sdk-go/service/ssm"
)
-const SSM_MAX_SIZE = 4096
-
-// ((16^4)*4096)/1024/1024
-// If we ever need more than 256 MB in parameter store, we've done something
-// very wrong.
-const SSM_KEY_FORMAT = "%s-%04X" //
-
var (
- KMS_KEY_ID *string = nil
- VersionString = "development"
+ VersionString = "development"
svc *ssm.SSM
Logger = log.New(os.Stderr, "", 0)
- trimRegex = regexp.MustCompile("-[0-9A-E][0-9A-E][0-9A-E][0-9A-E]$")
keyPrefix = os.Getenv("DPW_SSM_PREFIX")
)
+func getPath(params []string) string {
+ if len(params) != 1 {
+ Logger.Printf("Params provided: '%s'", params)
+ Logger.Fatal("Expected exactly one parameter, the path")
+ }
+ return keyPrefix + params[0]
+}
+
func listParams(params []string) {
info, err := store.GetInfo(svc)
if err != nil {
Logger.Fatal(err)
}
+ s := []string{}
for key, _ := range info.ByKey {
+ s = append(s, key)
+ }
+ sort.Strings(s)
+
+ for _, key := range s {
// Skip over things that aren't prefixed...
if keyPrefix != "" && !strings.HasPrefix(key, keyPrefix) {
continue
}
fmt.Println(strings.TrimPrefix(key, keyPrefix))
}
-
- os.Exit(0)
}
func insertParam(params []string) {
- if len(params) != 1 {
- Logger.Printf("Params provided: '%s'", params)
- Logger.Fatal("Expected exactly one parameter, the path")
- }
- path := keyPrefix + params[0]
+ path := getPath(params)
err := store.InsertParam(svc, os.Stdin, path)
if err != nil {
Logger.Fatalf("While inserting: '%s': %s", path, err)
}
-
- os.Exit(0)
}
func showParam(params []string) {
- if len(params) != 1 {
- Logger.Printf("Params provided: '%s'", params)
- Logger.Fatal("Expected exactly one parameter, the path")
- }
- path := keyPrefix + params[0]
+ path := getPath(params)
err := store.GetParam(svc, os.Stdout, path)
if err != nil {
Logger.Fatalf("Encountered: %s\n", err)
}
-
- os.Exit(0)
}
func removeParam(params []string) {
- if len(params) != 1 {
- Logger.Printf("Params provided: '%s'", params)
- Logger.Fatal("Expected exactly one parameter, the path")
- }
- path := keyPrefix + params[0]
+ path := getPath(params)
err := store.RemoveParam(svc, path)
if err != nil {
Logger.Fatalf("Encountered: %s\n", err)
}
-
- os.Exit(0)
}
func help() {
fmt.Printf(`
dpw-ssm: An AWS SSM backend for the dynamic password manager.
-https://git.riedstra.dev/mitch/dpw/about/
This can be used directly, but for interactive use 'dpw' is encouraged.
+AWS console:
+https://%s.console.aws.amazon.com/systems-manager/parameters
+
Available commands:
list
@@ -111,14 +100,14 @@ Debugging environment variables:
DPW_SSM_DEBUG=YES # Enable extended logging
-Environment variables:
+Example of available environment variables:
DPW_SSM_PREFIX=<prefix for all keys>
DPW_SSM_KMS_KEY_ID=<KMS KEY ID> # Optional
DPW_SSM_TAGS='{"json":"encoded","set":"of","key":"value","pairs":"..."}'
version: %s
-`, VersionString)
+`, os.Getenv("AWS_REGION"), VersionString)
os.Exit(0)
}
@@ -135,15 +124,7 @@ func setRegion() {
}
}
-func main() {
- if os.Getenv("DPW_SSM_DEBUG") != "" {
- Logger = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)
- }
-
- if os.Getenv("DPW_SSM_KMS_KEY_ID") != "" {
- store.KMS_KEY_ID = aws.String(os.Getenv("DPW_SSM_KMS_KEY_ID"))
- }
-
+func setupTags() {
ssm_tags_json := os.Getenv("DPW_SSM_TAGS")
if ssm_tags_json != "" {
tags := map[string]string{}
@@ -159,32 +140,41 @@ func main() {
}
}
}
+}
+
+func main() {
+ if os.Getenv("DPW_SSM_DEBUG") != "" {
+ Logger = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)
+ }
+
+ if os.Getenv("DPW_SSM_KMS_KEY_ID") != "" {
+ store.KMS_KEY_ID = aws.String(os.Getenv("DPW_SSM_KMS_KEY_ID"))
+ }
+ setupTags()
setRegion()
ses := session.Must(session.NewSession())
svc = ssm.New(ses)
- for n, arg := range os.Args[1:] {
- switch arg {
- case "list":
- listParams(os.Args[n+2:])
- break
- case "insert":
- insertParam(os.Args[n+2:])
- break
- case "show":
- showParam(os.Args[n+2:])
- break
- case "rm":
- removeParam(os.Args[n+2:])
- break
- case "init":
- fmt.Fprintln(os.Stderr, "No init process is necessary")
- break
- default:
- fmt.Fprintf(os.Stderr, "Unknown argument: '%s'\n", arg)
- help()
- }
+ switch os.Args[1] {
+ case "list":
+ listParams(os.Args[2:])
+ break
+ case "insert":
+ insertParam(os.Args[2:])
+ break
+ case "show":
+ showParam(os.Args[2:])
+ break
+ case "rm":
+ removeParam(os.Args[2:])
+ break
+ case "init":
+ fmt.Fprintln(os.Stderr, "No init process is necessary")
+ break
+ default:
+ fmt.Fprintf(os.Stderr, "Unknown argument: '%s'\n", os.Args[1])
+ help()
}
}
diff --git a/readme.md b/readme.md
index fa3d253..3a35054 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-# dpw-ssm or a basic tool to make storing secrets in SSM a little easier.
+# dpw-ssm or a basic tool to make storing secrets in AWS SSM Parameter Store a little easier.
This is fundamentally a plugin for [https://git.riedstra.dev/mitch/dpw/about/](
https://git.riedstra.dev/mitch/dpw/about/)
@@ -9,6 +9,9 @@ I've only spent a few hours on it, so expect an edge case or two, but for my
purposes of easily using the parameter store for things larger than 4K and small
binaries it works wonderfully.
+You can read up a bit more on the [AWS service
+here](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html).
+
## Building
```
diff --git a/store/store.go b/store/store.go
index fc42193..ece4040 100644
--- a/store/store.go
+++ b/store/store.go
@@ -1,3 +1,9 @@
+// Store is not designed to be used as a database, or some high intensity
+// key/value store, rather a low volume ad-hoc key value store for secrets
+// inside of AWS.
+//
+// This should work out of the box with pretty much every AWS account.
+// See also the bundled program.
package store
import (
@@ -11,24 +17,30 @@ import (
"github.com/aws/aws-sdk-go/service/ssm"
)
-const SSM_MAX_SIZE = 4096
+const SSM_MAX_SIZE = 4096 // This is dictated by AWS
-// ((16^4)*4096)/1024/1024
-// If we ever need more than 256 MB in parameter store, we've done something
-// very wrong.
-const SSM_KEY_FORMAT = "%s-%04X" //
+const SSM_KEY_FORMAT = "%s-%04X" // Should be good up to 256MB ( 16^4 bytes... )
var (
// TrimRegex is used to group the SSM keys inside of the Info struct under
- // ByKey. This will only be used for params that exceed 4KB.
+ // ByKey. This is used so that we can have keys larger than 4KB.
+ // See also SSM_KEY_FORMAT
TrimRegex = regexp.MustCompile("-[0-9A-E][0-9A-E][0-9A-E][0-9A-E]$")
- // Optional, can be set set to utilize a specific KMS key if desired.
+ // KMS_KEY_ID is optional, can be set set to utilize a specific KMS key if
+ // desired.
KMS_KEY_ID *string = nil
+ // Tags are also optional, if used with the bundled program you can
+ // simply set an environment variable. Otherwise, set them here
+ // at the package level.
Tags = []*ssm.Tag{}
)
+// Info contains a few maps with pointers to all of the parameters, setup with
+// different keys for easy lookup. `ByKey` is what you'd expect. `ByFullKey`
+// has a dash and four hex digits appended to it for entries larger than 4K
+// and actually reflects the keys you'll see in the parameter store console.
type Info struct {
ByKey map[string]*Entry
ByFullKey map[string]*Entry
@@ -65,12 +77,14 @@ func (i *Info) add(e *ssm.ParameterMetadata) {
i.ByFullKey[*e.Name] = entry
}
+// Entry represents an entry in the store, and all of the actual parameters
+// that it spans
type Entry struct {
Name string
Keys []*ssm.ParameterMetadata
}
-// GetInfo returns a populated Info struct from the SSM
+// GetInfo returns a populated Info struct from the parameter store.
func GetInfo(svc *ssm.SSM) (*Info, error) {
ret := &Info{
ByKey: map[string]*Entry{},
@@ -97,6 +111,8 @@ func GetInfo(svc *ssm.SSM) (*Info, error) {
return ret, nil
}
+// InsertParam will chuck data from the rdr into the parameter store under
+// key, automatically chunking it into multiple parameters as needed.
func InsertParam(svc *ssm.SSM, rdr io.Reader, key string) error {
buf := &bytes.Buffer{}
enc := base64.NewEncoder(base64.StdEncoding, buf)
@@ -144,6 +160,9 @@ func InsertParam(svc *ssm.SSM, rdr io.Reader, key string) error {
return nil
}
+// GetParam will suck data out of parameter store for a key, automatically
+// collecting all of the individual parameters needed to reconstruct the data
+// and writes it out to the io.Writer
func GetParam(svc *ssm.SSM, wrtr io.Writer, key string) error {
n := 1
buf := &bytes.Buffer{}
@@ -178,6 +197,8 @@ func GetParam(svc *ssm.SSM, wrtr io.Writer, key string) error {
return nil
}
+// RemoveParam takes care of collecting all of the pieces for a given key,
+// and removes all of them from the parameter store
func RemoveParam(svc *ssm.SSM, key string) error {
info, err := GetInfo(svc)
if err != nil {