package main import ( "encoding/json" "fmt" "log" "os" "regexp" "strings" "riedstra.dev/go/dpw-ssm/store" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "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" 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 listParams(params []string) { info, err := store.GetInfo(svc) if err != nil { Logger.Fatal(err) } for key, _ := range info.ByKey { // 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] 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] 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] 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. Available commands: list insert show rm Debugging environment variables: DPW_SSM_DEBUG=YES # Enable extended logging Environment variables: DPW_SSM_PREFIX= DPW_SSM_KMS_KEY_ID= # Optional DPW_SSM_TAGS='{"json":"encoded","set":"of","key":"value","pairs":"..."}' version: %s `, VersionString) os.Exit(0) } func setRegion() { if os.Getenv("AWS_REGION") == "" { // Default to us-east-2 os.Setenv("AWS_REGION", "us-east-2") // But if a default is set, respect that, since the AWS SDK for Go // doesn't, normally. if os.Getenv("AWS_DEFAULT_REGION") != "" { os.Setenv("AWS_REGION", os.Getenv("AWS_DEFAULT_REGION")) } } } 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")) } ssm_tags_json := os.Getenv("DPW_SSM_TAGS") if ssm_tags_json != "" { tags := map[string]string{} err := json.Unmarshal([]byte(ssm_tags_json), &tags) if err != nil { Logger.Println("Warning, failed to decode DPW_SSM_TAGS: %s\n", err) } else { for k, v := range tags { store.Tags = append(store.Tags, &ssm.Tag{ Key: aws.String(k), Value: aws.String(v), }) } } } 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() } } }