diff --git a/CHANGELOG.md b/CHANGELOG.md index 0531014b..acb2901d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [0.3.4] - 2018-10-11 + +### Added + +* Adds new mode aws-sec-ssm which is using ssm parameters type SEcureString which can be automatically decrypted by AWS KMS key +* Update Dockerfile to multistage build format + ## [0.3.0] - 2017-09-03 ### Added diff --git a/Dockerfile b/Dockerfile index 08a8d061..59251181 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,16 @@ -FROM alpine:3.6 +FROM golang:1.10.3 as builder -RUN apk add --update ca-certificates +ARG CI_COMMIT_TAG +ARG CI_COMMIT_SHA +ARG CI_DATE + +COPY . /go/src/github.com/jetstack/vault-unsealer +WORKDIR /go/src/github.com/jetstack/vault-unsealer -COPY vault-unsealer_linux_amd64 /usr/local/bin/vault-unsealer +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags "-w -X main.version=${CI_COMMIT_TAG} -X main.commit=${CI_COMMIT_SHA} -X main.date=${CI_DATE}" -o vault-unsealer + +FROM alpine:3.6 +RUN apk add --update ca-certificates +COPY --from=builder /go/src/github.com/jetstack/vault-unsealer/vault-unsealer /usr/local/bin/vault-unsealer -ENTRYPOINT ["/usr/local/bin/vault-unsealer"] +ENTRYPOINT ["/usr/local/bin/vault-unsealer"] \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock index cfbe30b2..baa8f24d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,234 +3,464 @@ [[projects]] branch = "master" + digest = "1:44d2c03bc8126d8d5ed8bc7ef37efbf09d169ca2196fcc16a8dd66fb56878bce" name = "cloud.google.com/go" - packages = ["compute/metadata","iam","internal","internal/optional","internal/version","storage"] + packages = [ + "compute/metadata", + "iam", + "internal", + "internal/optional", + "internal/version", + "storage", + ] + pruneopts = "" revision = "ae792a8babd4ec16f4b1677026d838411d8b2c79" [[projects]] + digest = "1:99080c23669518951a484330eddad059c79962ea0f029f241e4437d7dd5c7e6f" name = "github.com/aws/aws-sdk-go" - packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/json/jsonutil","private/protocol/jsonrpc","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/xml/xmlutil","service/kms","service/ssm","service/sts"] + packages = [ + "aws", + "aws/awserr", + "aws/awsutil", + "aws/client", + "aws/client/metadata", + "aws/corehandlers", + "aws/credentials", + "aws/credentials/ec2rolecreds", + "aws/credentials/endpointcreds", + "aws/credentials/stscreds", + "aws/defaults", + "aws/ec2metadata", + "aws/endpoints", + "aws/request", + "aws/session", + "aws/signer/v4", + "internal/shareddefaults", + "private/protocol", + "private/protocol/json/jsonutil", + "private/protocol/jsonrpc", + "private/protocol/query", + "private/protocol/query/queryutil", + "private/protocol/rest", + "private/protocol/xml/xmlutil", + "service/kms", + "service/ssm", + "service/sts", + ] + pruneopts = "" revision = "1b176c5c6b57adb03bb982c21930e708ebca5a77" version = "v1.12.70" [[projects]] + digest = "1:55848e643a99a9dfceb19e090ce67111328fbb1780f34c62a0430994ff85fb90" name = "github.com/fatih/structs" packages = ["."] + pruneopts = "" revision = "a720dfa8df582c51dee1b36feabb906bde1588bd" version = "v1.0" [[projects]] + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "github.com/fsnotify/fsnotify" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] + digest = "1:a00483fe4106b86fb1187a92b5cf6915c85f294ed4c129ccbe7cb1f1a06abd46" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" version = "v1.32.0" [[projects]] + digest = "1:bcb38c8fc9b21bb8682ce2d605a7d4aeb618abc7f827e3ac0b27c0371fdb23fb" name = "github.com/golang/protobuf" - packages = ["proto","protoc-gen-go/descriptor","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"] + packages = [ + "proto", + "protoc-gen-go/descriptor", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "" revision = "925541529c1fa6821df4e44ce2723319eb2be768" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:09307dfb1aa3f49a2bf869dcfa4c6c06ecd3c207221bd1c1a1141f0e51f209eb" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "553a641470496b2327abcac10b36396bd98e45c9" [[projects]] + digest = "1:e097a364f4e8d8d91b9b9eeafb992d3796a41fde3eb548c1a87eb9d9f60725cf" name = "github.com/googleapis/gax-go" packages = ["."] + pruneopts = "" revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" version = "v2.0.0" [[projects]] branch = "master" + digest = "1:304c322b62533a48ac052ffee80f67087fce1bc07186cd4e610a1b0e77765836" name = "github.com/hashicorp/errwrap" packages = ["."] + pruneopts = "" revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55" [[projects]] branch = "master" + digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] + pruneopts = "" revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d" [[projects]] branch = "master" + digest = "1:b46ef59de1f724e8a2b508ea2b329eaf6cac4d71cbd44ad5e3dbd4e8fd49de9b" name = "github.com/hashicorp/go-multierror" packages = ["."] + pruneopts = "" revision = "b7773ae218740a7be65057fc60b366a49b538a44" [[projects]] branch = "master" + digest = "1:ff65bf6fc4d1116f94ac305342725c21b55c16819c2606adc8f527755716937f" name = "github.com/hashicorp/go-rootcerts" packages = ["."] + pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] branch = "master" + digest = "1:147d671753effde6d3bcd58fc74c1d67d740196c84c280c762a5417319499972" name = "github.com/hashicorp/hcl" - packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"] + packages = [ + ".", + "hcl/ast", + "hcl/parser", + "hcl/scanner", + "hcl/strconv", + "hcl/token", + "json/parser", + "json/scanner", + "json/token", + ] + pruneopts = "" revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8" [[projects]] branch = "master" + digest = "1:d416038e340ad697bbf4d8c15e4acd077f851971ff940f006f227692243bc5a5" name = "github.com/hashicorp/vault" - packages = ["api","helper/compressutil","helper/jsonutil","helper/parseutil"] + packages = [ + "api", + "helper/compressutil", + "helper/jsonutil", + "helper/parseutil", + ] + pruneopts = "" revision = "8ca8b46c51847bd721a8b534925cc8f8f3ea68e4" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] + digest = "1:739b2038a38cebb50e922d18f4b042c042256320fea2db094814aeef8891e0c1" name = "github.com/magiconair/properties" packages = ["."] + pruneopts = "" revision = "d419a98cdbed11a922bf76f257b7c4be79b50e73" version = "v1.7.4" [[projects]] branch = "master" + digest = "1:59d11e81d6fdd12a771321696bb22abdd9a94d26ac864787e98c9b419e428734" name = "github.com/mitchellh/go-homedir" packages = ["."] + pruneopts = "" revision = "b8bc1bf767474819792c23f32d8286a45736f1c6" [[projects]] branch = "master" + digest = "1:a31c8ff06bf8ccc1826e3a28a4132bf217a53f3b096e6a03dcac7b6a9eb796f9" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "b4575eea38cca1123ec2dc90c26529b5c5acfcff" [[projects]] + digest = "1:d60cfeee185019d4fcd35e8c89c83aff576e4723b6100300bf67b05be961388f" name = "github.com/pelletier/go-toml" packages = ["."] + pruneopts = "" revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:0bbb3483b2d9386cca8af0d101b636aec54d6f425b8bb1037c69c427c83c4b42" name = "github.com/sethgrid/pester" packages = ["."] + pruneopts = "" revision = "760f8913c0483b776294e1bee43f1d687527127b" [[projects]] + digest = "1:42a42c4bc67bed17f40fddf0f24d4403e25e7b96488456cf4248e6d16659d370" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "" revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba" version = "v1.0.4" [[projects]] + digest = "1:dae0d7dd55563fd389e7263a32d2030022ef29cceff941336e53f6520e0308c0" name = "github.com/spf13/afero" - packages = [".","mem"] + packages = [ + ".", + "mem", + ] + pruneopts = "" revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c" version = "v1.0.2" [[projects]] + digest = "1:6ff9b74bfea2625f805edec59395dc37e4a06458dd3c14e3372337e3d35a2ed6" name = "github.com/spf13/cast" packages = ["."] + pruneopts = "" revision = "acbeb36b902d72a7a4c18e8f3241075e7ab763e4" version = "v1.1.0" [[projects]] + digest = "1:2208a80fc3259291e43b30f42f844d18f4218036dff510f42c653ec9890d460a" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "" revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" version = "v0.0.1" [[projects]] branch = "master" + digest = "1:104517520aab91164020ab6524a5d6b7cafc641b2e42ac6236f6ac1deac4f66a" name = "github.com/spf13/jwalterweatherman" packages = ["."] + pruneopts = "" revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" [[projects]] + digest = "1:261bc565833ef4f02121450d74eb88d5ae4bd74bfe5d0e862cddb8550ec35000" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "" revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" version = "v1.0.0" [[projects]] + digest = "1:59354ad53dfe6ed1b941844cb029cd37c0377598eec3a0d49c03aee2375ef9c4" name = "github.com/spf13/viper" packages = ["."] + pruneopts = "" revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:febc7d7c1a817016b83441742910a3eab682d5c64613fcd91fde7ca06107ea49" name = "golang.org/x/crypto" packages = ["ssh/terminal"] + pruneopts = "" revision = "1875d0a70c90e57f11972aefd42276df65e895b9" [[projects]] branch = "master" + digest = "1:57cdbf612d6928a01850051b5c09972262244ae131a40dfea4c37fd525742db5" name = "golang.org/x/net" - packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"] + packages = [ + "context", + "context/ctxhttp", + "http2", + "http2/hpack", + "idna", + "internal/timeseries", + "lex/httplex", + "trace", + ] + pruneopts = "" revision = "0ed95abb35c445290478a5348a7b38bb154135fd" [[projects]] branch = "master" + digest = "1:36417e4645d439fcd9cf732f42006b9bfce0d9dc6118d9616819a031a349bb5b" name = "golang.org/x/oauth2" - packages = [".","google","internal","jws","jwt"] + packages = [ + ".", + "google", + "internal", + "jws", + "jwt", + ] + pruneopts = "" revision = "a032972e28060ca4f5644acffae3dfc268cc09db" [[projects]] branch = "master" + digest = "1:e4853644a857f4dd587ac56235c24ab32fefa393a8955be2ff5a535b820d5e19" name = "golang.org/x/sys" - packages = ["unix","windows"] + packages = [ + "unix", + "windows", + ] + pruneopts = "" revision = "3dbebcf8efb6a5011a60c2b4591c1022a759af8a" [[projects]] branch = "master" + digest = "1:9f9562b058a594dae0c8e2388c1a802664111736845bc08885246f0e4bf99bdd" name = "golang.org/x/text" - packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] + packages = [ + "collate", + "collate/build", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "language", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + ] + pruneopts = "" revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3" [[projects]] branch = "master" + digest = "1:4b288a37811ddbb5a3aa8aebadaf3e3845c9de1aeb90a4d22475411c524b37d4" name = "google.golang.org/api" - packages = ["cloudkms/v1","gensupport","googleapi","googleapi/internal/uritemplates","googleapi/transport","internal","iterator","option","storage/v1","transport/http"] + packages = [ + "cloudkms/v1", + "gensupport", + "googleapi", + "googleapi/internal/uritemplates", + "googleapi/transport", + "internal", + "iterator", + "option", + "storage/v1", + "transport/http", + ] + pruneopts = "" revision = "ffa5046912fde3e38fd41244f9593c99431ecfa2" [[projects]] + digest = "1:934fb8966f303ede63aa405e2c8d7f0a427a05ea8df335dfdc1833dd4d40756f" name = "google.golang.org/appengine" - packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] + packages = [ + ".", + "internal", + "internal/app_identity", + "internal/base", + "internal/datastore", + "internal/log", + "internal/modules", + "internal/remote_api", + "internal/urlfetch", + "urlfetch", + ] + pruneopts = "" revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:93d113349a5f430d3af09a200ccbe6fea38a44e18a9b7dc032e6eadb75cd1cb2" name = "google.golang.org/genproto" - packages = ["googleapis/api/annotations","googleapis/iam/v1","googleapis/rpc/status"] + packages = [ + "googleapis/api/annotations", + "googleapis/iam/v1", + "googleapis/rpc/status", + ] + pruneopts = "" revision = "4eb30f4778eed4c258ba66527a0d4f9ec8a36c45" [[projects]] + digest = "1:1c1ce3d4783796c0922d16dec8b0e9be02d383888e9126718e5a5db8baa3fe6d" name = "google.golang.org/grpc" - packages = [".","balancer","balancer/base","balancer/roundrobin","codes","connectivity","credentials","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"] + packages = [ + ".", + "balancer", + "balancer/base", + "balancer/roundrobin", + "codes", + "connectivity", + "credentials", + "encoding", + "grpclb/grpc_lb_v1/messages", + "grpclog", + "internal", + "keepalive", + "metadata", + "naming", + "peer", + "resolver", + "resolver/dns", + "resolver/passthrough", + "stats", + "status", + "tap", + "transport", + ] + pruneopts = "" revision = "6b51017f791ae1cfbec89c52efdf444b13b550ef" version = "v1.9.2" [[projects]] branch = "v2" + digest = "1:4b4e5848dfe7f316f95f754df071bebfb40cf4482da62e17e7e1aebdf11f4918" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "1daea21c293f583ed8049d95e6731b7377b1c81010bff0772806002d7681889a" + input-imports = [ + "cloud.google.com/go/storage", + "github.com/aws/aws-sdk-go/aws", + "github.com/aws/aws-sdk-go/aws/session", + "github.com/aws/aws-sdk-go/service/kms", + "github.com/aws/aws-sdk-go/service/ssm", + "github.com/hashicorp/vault/api", + "github.com/sirupsen/logrus", + "github.com/spf13/cobra", + "github.com/spf13/viper", + "golang.org/x/oauth2/google", + "google.golang.org/api/cloudkms/v1", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Makefile b/Makefile index 06425d63..03b53cad 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,8 @@ -GO_PKG = gitlab.jetstack.net/jetstack/vault-unsealer - REGISTRY := quay.io/jetstack IMAGE_NAME := vault-unsealer BUILD_TAG := build IMAGE_TAGS := canary -BUILD_IMAGE_NAME := golang:1.10.4 - -GOPATH ?= /tmp/go - CI_COMMIT_TAG ?= unknown CI_COMMIT_SHA ?= unknown @@ -16,6 +10,8 @@ help: # all - runs verify, build and docker_build targets # test - runs go_test target # build - runs go_build target + # docker - build local version of vault-unsealer + # release - build application release in container and push it to repo # verify - verifies generated files & scripts # Util targets @@ -24,28 +20,22 @@ help: all: verify build docker_build +test: go_test + build: go_build -verify: go_verify +docker: docker_build -.builder_image: - docker pull ${BUILD_IMAGE_NAME} +release: verify docker_build docker_push -# Builder image targets -####################### -docker_%: .builder_image - docker run -it \ - -v ${GOPATH}/src:/go/src \ - -v $(shell pwd):/go/src/${GO_PKG} \ - -w /go/src/${GO_PKG} \ - -e GOPATH=/go \ - ${BUILD_IMAGE_NAME} \ - /bin/sh -c "make $*" +verify: go_verify # Docker targets ################ docker_build: - docker build -t $(REGISTRY)/$(IMAGE_NAME):$(BUILD_TAG) . + docker build -t $(REGISTRY)/$(IMAGE_NAME):$(BUILD_TAG) --build-arg CI_COMMIT_TAG=${CI_COMMIT_TAG} \ + --build-arg CI_COMMIT_SHA=${CI_COMMIT_SHA} --build-arg CI_DATE=$(shell date -u +%Y-%m-%dT%H:%M:%SZ) \ + -f Dockerfile . docker_push: docker_build set -e; \ @@ -59,7 +49,9 @@ docker_push: docker_build go_verify: go_fmt go_vet go_test go_build: - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w -X main.version=$(CI_COMMIT_TAG) -X main.commit=$(CI_COMMIT_SHA) -X main.date=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)' -o vault-unsealer_linux_amd64 + CGO_ENABLED=0 go build -a -tags netgo -ldflags '-w -X main.version=$(CI_COMMIT_TAG) \ + -X main.commit=$(CI_COMMIT_SHA) -X main.date=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)' \ + -o vault-unsealer go_test: go test $$(go list ./... | grep -v '/vendor/') diff --git a/README.md b/README.md index 5ad92581..08937c02 100755 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This is a CLI tool to help automate the setup and management of Hashicorp Vault. It will continuously attempt to unseal the target Vault instance, by retrieving -unseal keys from a Google Cloud KMS keyring. +unseal keys from a Google/AWS Cloud KMS keyring. Usage: vault-unsealer [command] @@ -30,7 +30,7 @@ Flags: --google-cloud-storage-bucket string The name of the Google Cloud Storage bucket to store values in --google-cloud-storage-prefix string The prefix to use for values store in Google Cloud Storage -h, --help help for vault-unsealer - --mode string Select the mode to use 'google-cloud-kms-gcs' => Google Cloud Storage with encryption using Google KMS; 'aws-kms-ssm' => AWS SSM parameter store using AWS KMS encryption (default "google-cloud-kms-gcs") + --mode string Select the mode to use 'google-cloud-kms-gcs' => Google Cloud Storage with encryption using Google KMS; 'aws-kms-ssm' => AWS SSM parameter store using AWS KMS encryption; 'aws-sec-ssm' => AWS SSM parameter mode using Secure strings and KMS key for encryption (default "google-cloud-kms-gcs") --secret-shares int Total count of secret shares that exist (default 1) --secret-threshold int Minimum required secret shares to unseal (default 1) @@ -53,3 +53,9 @@ make -C $(go env GOPATH)/src/github.com/jetstack/vault-unsealer build ```bash docker build -t vault-unsealer: . ``` + +## TODO + +1) Add support for kubernetes leader election + + https://github.com/kubernetes/contrib/tree/master/election \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index aa6fde1a..85836138 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,6 +17,7 @@ const cfgSecretThreshold = "secret-threshold" const cfgMode = "mode" const cfgModeValueAWSKMSSSM = "aws-kms-ssm" +const cfgModeValueAWSSECSSM = "aws-sec-ssm" const cfgModeValueGoogleCloudKMSGCS = "google-cloud-kms-gcs" const cfgModeValueLocal = "local" @@ -78,7 +79,7 @@ func init() { configStringVar( cfgMode, cfgModeValueGoogleCloudKMSGCS, - fmt.Sprintf("Select the mode to use '%s' => Google Cloud Storage with encryption using Google KMS; '%s' => AWS SSM parameter store using AWS KMS encryption; %s => Use local keys in path", cfgModeValueGoogleCloudKMSGCS, cfgModeValueAWSKMSSSM, cfgModeValueLocal), + fmt.Sprintf("Select the mode to use '%s' => Google Cloud Storage with encryption using Google KMS; '%s' => AWS SSM parameter store using AWS KMS encryption; '%s' => AWS SSM parameter mode using Secure strings and KMS key for encryption; %s => Use local keys in path", cfgModeValueGoogleCloudKMSGCS, cfgModeValueAWSKMSSSM, cfgModeValueAWSSECSSM, cfgModeValueLocal), ) // Secret config diff --git a/cmd/util.go b/cmd/util.go index 85e9c7a9..338f7ff8 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -7,6 +7,7 @@ import ( "github.com/jetstack/vault-unsealer/pkg/kv" "github.com/jetstack/vault-unsealer/pkg/kv/aws_kms" + "github.com/jetstack/vault-unsealer/pkg/kv/aws_sec_ssm" "github.com/jetstack/vault-unsealer/pkg/kv/aws_ssm" "github.com/jetstack/vault-unsealer/pkg/kv/cloudkms" "github.com/jetstack/vault-unsealer/pkg/kv/gcs" @@ -69,6 +70,13 @@ func kvStoreForConfig(cfg *viper.Viper) (kv.Service, error) { } return kms, nil + case cfgModeValueAWSSECSSM: + ssm, err := aws_sec_ssm.New(cfg.GetString(cfgAWSSSMKeyPrefix), cfg.GetString(cfgAWSKMSKeyID)) + if err != nil { + return nil, fmt.Errorf("error creating AWS SSM using Secure parameters kv store: %s", err.Error()) + } + + return ssm, nil case cfgModeValueLocal: return local.New(cfg.GetString(cfgLocalKeyDir)) diff --git a/docs/vault/.gitignore b/docs/vault/.gitignore new file mode 100644 index 00000000..c475f0a7 --- /dev/null +++ b/docs/vault/.gitignore @@ -0,0 +1,2 @@ +.env +.terraform \ No newline at end of file diff --git a/docs/vault/variables.tf b/docs/vault/variables.tf new file mode 100644 index 00000000..9c09c39f --- /dev/null +++ b/docs/vault/variables.tf @@ -0,0 +1,21 @@ +variable "environment" { + description = "Environment tag description" + default = "dev" +} + +variable "vault_unseal_key0" { + type = "string" + description = "" +} +variable "vault_unseal_key1" { + type = "string" + description = "" +} +variable "vault_unseal_key2" { + type = "string" + description = "" +} +variable "vault_unseal_key3" { + type = "string" + description = "" +} \ No newline at end of file diff --git a/docs/vault/vault.tf b/docs/vault/vault.tf new file mode 100644 index 00000000..65aa7960 --- /dev/null +++ b/docs/vault/vault.tf @@ -0,0 +1,75 @@ +terraform { + backend "s3" { + bucket = "vault-store" + key = "terraform.vault.state" + region = "us-east-1" + } +} + +provider "aws" { + region = "eu-west-1" +} + +resource "aws_kms_key" "vault_kms_key" { + description = "Vault dev kms key" + tags { + Environment = "${var.environment}" + Terraform = "true" + } +} + +resource "aws_kms_alias" "vault_kms_key_alias" { + name = "alias/${var.environment}-vault" + target_key_id = "${aws_kms_key.vault_kms_key.key_id}" +} + +resource "aws_ssm_parameter" "vault-unseal-0" { + name = "${var.environment}-vault-unseal-0" + description = "Vault Unseal key number 0" + type = "SecureString" + value = "${var.vault_unseal_key0}" + key_id = "${aws_kms_key.vault_kms_key.key_id}" + + tags { + Environment = "${var.environment}" + Terraform = "true" + } +} +resource "aws_ssm_parameter" "vault-unseal-1" { + name = "${var.environment}-vault-unseal-1" + description = "Vault Unseal key number 1" + type = "SecureString" + value = "${var.vault_unseal_key1}" + key_id = "${aws_kms_key.vault_kms_key.key_id}" + + tags { + Environment = "${var.environment}" + Terraform = "true" + } +} + +resource "aws_ssm_parameter" "vault-unseal-2" { + name = "${var.environment}-vault-unseal-2" + description = "Vault Unseal key number 2" + type = "SecureString" + value = "${var.vault_unseal_key2}" + key_id = "${aws_kms_key.vault_kms_key.key_id}" + + tags { + Environment = "${var.environment}" + Terraform = "true" + } +} + +resource "aws_ssm_parameter" "vault-unseal-3" { + name = "${var.environment}-vault-unseal-3" + description = "Vault Unseal key number 3" + type = "SecureString" + value = "${var.vault_unseal_key3}" + key_id = "${aws_kms_key.vault_kms_key.key_id}" + + tags { + Environment = "${var.environment}" + Terraform = "true" + } +} \ No newline at end of file diff --git a/pkg/kv/aws_sec_ssm/aws_sec_ssm.go b/pkg/kv/aws_sec_ssm/aws_sec_ssm.go new file mode 100644 index 00000000..10cfcee2 --- /dev/null +++ b/pkg/kv/aws_sec_ssm/aws_sec_ssm.go @@ -0,0 +1,83 @@ +package aws_sec_ssm + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ssm" + + "github.com/jetstack/vault-unsealer/pkg/kv" +) + +type awsSSM struct { + ssmService *ssm.SSM + + keyPrefix string + kmsID string +} + +var _ kv.Service = &awsSSM{} + +func NewWithSession(sess *session.Session, keyPrefix string, kmsID string) (*awsSSM, error) { + return &awsSSM{ + ssmService: ssm.New(sess), + keyPrefix: keyPrefix, + kmsID: kmsID, + }, nil +} + +func New(keyPrefix string, kmsID string) (*awsSSM, error) { + sess, err := session.NewSession() + if err != nil { + return nil, err + } + + return NewWithSession(sess, keyPrefix, kmsID) +} + +func (a *awsSSM) Get(key string) ([]byte, error) { + out, err := a.ssmService.GetParameters(&ssm.GetParametersInput{ + Names: []*string{ + aws.String(a.name(key)), + }, + WithDecryption: aws.Bool(true), + }) + if err != nil { + return []byte{}, err + } + + if len(out.Parameters) < 1 { + return []byte{}, kv.NewNotFoundError("key '%s' not found", key) + } + + return []byte(*out.Parameters[0].Value), nil +} + +func (a *awsSSM) name(key string) string { + return fmt.Sprintf("%s%s", a.keyPrefix, key) +} + +func (a *awsSSM) Set(key string, val []byte) error { + _, err := a.ssmService.PutParameter(&ssm.PutParameterInput{ + Description: aws.String("vault-unsealer"), + Name: aws.String(a.name(key)), + Overwrite: aws.Bool(true), + Value: aws.String(string(val)), + Type: aws.String("SecureString"), + KeyId: aws.String(a.kmsID), + }) + return err +} + +func (a *awsSSM) Delete(key string) error { + _, err := a.ssmService.DeleteParameter(&ssm.DeleteParameterInput{ + Name: aws.String(a.name(key)), + }) + return err +} + +func (g *awsSSM) Test(key string) error { + // TODO: Implement a test if a Set is likely to work, AWS doesn't seemt to provide a dry-run on the parameter store + return nil +} diff --git a/pkg/kv/aws_sec_ssm/aws_sec_ssm_test.go b/pkg/kv/aws_sec_ssm/aws_sec_ssm_test.go new file mode 100644 index 00000000..1057002c --- /dev/null +++ b/pkg/kv/aws_sec_ssm/aws_sec_ssm_test.go @@ -0,0 +1,62 @@ +package aws_sec_ssm + +import ( + "os" + "testing" + + "github.com/jetstack/vault-unsealer/pkg/kv" +) + +func TestAWSIntegration(t *testing.T) { + region := os.Getenv("AWS_REGION") + + if region == "" { + t.Skip("Skip AWS integration tests: not environment variable 'AWS_REGION' specified") + } + + payloadKey := "test123" + payloadValue := "payload123" + + a, err := New("test-integration-", "TestKMSIDkey") + if err != nil { + t.Errorf("Unexpected error creating SSM kv: %s", err) + } + + // graceful set (in case it's already existing) + err = a.Set(payloadKey, []byte(payloadValue)) + if err != nil { + t.Errorf("Unexpected error storing value in SSM kv: %s", err) + } + + // this should also work and overwrite a key + err = a.Set(payloadKey, []byte(payloadValue)) + if err != nil { + t.Errorf("Unexpected error storing value in SSM kv: %s", err) + } + + // this deletes the key + err = a.Delete(payloadKey) + if err != nil { + t.Errorf("Unexpected error storing value in SSM kv: %s", err) + } + + _, err = a.Get(payloadKey) + if _, ok := err.(*kv.NotFoundError); !ok { + t.Errorf("Expected an kv.NotFoundError for a non existing key") + } + + err = a.Set(payloadKey, []byte(payloadValue)) + if err != nil { + t.Errorf("Unexpected error storing value in SSM kv: %s", err) + } + + out, err := a.Get("test123") + if err != nil { + t.Errorf("Unexpected error storing value in SSM kv: %s", err) + } + + if exp, act := payloadValue, string(out); exp != act { + t.Errorf("Unexpected decrypt output: exp=%s act=%s", exp, act) + } + +}