diff --git a/Makefile b/Makefile index 13a5261..d21f014 100644 --- a/Makefile +++ b/Makefile @@ -36,4 +36,13 @@ fmtcheck: .PHONY: fmt fmt: - gofumpt -l -w . \ No newline at end of file + gofumpt -l -w . && cd bootstrap/terraform && terraform fmt + +.PHONY: setup-env +setup-env: + cd bootstrap/terraform && terraform init && terraform apply -auto-approve + + +.PHONY: teardown-env +teardown-env: + cd bootstrap/terraform && terraform init && terraform destroy -auto-approve \ No newline at end of file diff --git a/README.md b/README.md index b3724b1..427903a 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,21 @@ -# Vault Plugin Scaffolding +# Vault Plugin Database Redis ElastiCache -This is a standalone backend plugin for use with [Hashicorp +This is a standalone [Database Plugin](https://www.vaultproject.io/docs/secrets/databases) for use with [Hashicorp Vault](https://www.github.com/hashicorp/vault). -[//]: <> (Include a general statement about this plugin) +This plugin supports exclusively AWS ElastiCache for Redis. [Redis Enterprise](https://github.com/RedisLabs/vault-plugin-database-redis-enterprise) +and [Redis Open Source](https://github.com/fhitchen/vault-plugin-database-redis) use different plugins. Please note: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, please responsibly disclose by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). -## Using this Template Repository - -_Note: Remove this instruction sub-heading once you've created a repository from this template_ - -This repository is a template for a Vault secret engine and auth method plugins. -It is intended as a starting point for creating Vault plugins, containing: - -- Changelog, readme, Makefile, pull request template -- Scripts for internal tooling -- Jira sync and basic testing GitHub actions -- A base `main.go` for compiling the plugin - -There's some minimal GitHub Secrets setup required in order to get the Jira sync -GH action working. Install the `gh` [CLI](https://cli.github.com/manual/) and -perform the following commands to set secrets for this repository. - -```sh -gh secret set JIRA_SYNC_BASE_URL -gh secret set JIRA_SYNC_USER_EMAIL -gh secret set JIRA_SYNC_API_TOKEN -``` - - -This template repository does not include a Mozilla Public License 2.0 `LICENSE` -since plugins created this way can be internal to hashicorp and for Vault -Enterprise consumption. To add a license, follow [these GitHub -instructions](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-license-to-a-repository), -or obtain one from one of our public Vault plugins. - -Please see the [GitHub template repository -documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) -for how to create a new repository from this template on GitHub. - -Things _not_ handled by this template repository: -- Repository settings, such as branch protection rules -- Memberships and permissions -- GitHub secrets for this repository - -Please see the [Repository Configuration Page](https://hashicorp.atlassian.net/wiki/spaces/VAULT/pages/2103476333/Repository+Configuration) -for the setting proper repository configuration values. ## Quick Links - [Vault Website](https://www.vaultproject.io) -- [Vault Project GitHub](https://www.github.com/hashicorp/vault) +- [Plugin System](https://www.vaultproject.io/docs/plugins) -[//]: <> (Include any other quick links relevant to your plugin) ## Getting Started @@ -67,14 +27,17 @@ Otherwise, first read this guide on how to [get started with Vault](https://www.vaultproject.io/intro/getting-started/install.html). -## Usage +## Development -[//]: <> (Provide usage instructions and/or links to this plugin) +If you wish to work on this plugin, you'll first need +[Go](https://www.golang.org) installed on your machine (version 1.17+ recommended) -## Developing +Make sure Go is properly installed, including setting up a [GOPATH](https://golang.org/doc/code.html#GOPATH). -If you wish to work on this plugin, you'll first need -[Go](https://www.golang.org) installed on your machine. +To run the tests locally you will need to have write permissions to an [ElastiCache for Redis](https://aws.amazon.com/elasticache/redis/) instance. +A small Terraform project is included to provision one for you if needed. More details in the [Environment Set Up](#environment-set-up) section. + +## Building If you're developing for the first time, run `make bootstrap` to install the necessary tools. Bootstrap will also update repository name references if that @@ -92,6 +55,39 @@ mode will only generate the binary for your platform and is faster: $ make dev ``` +## Tests + +### Environment Set Up + +To test the plugin, you need access to an Elasticache for Redis Cluster. +A Terraform project is included for convenience to initialize a new cluster if needed. +If not already available, you can install Terraform by using [this documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). + +The setup script tries to find and use available AWS credentials from the environment. You can configure AWS credentials using [this documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). +Or if you prefer you can edit the provider defined ./bootstrap/terraform/elasticache.tf with your desired set of credentials. + +Note that resources created via the Terraform project cost a small amount of money per hour. + +To set up the test cluster: + +```sh +$ make setup-env +... +Apply complete! Resources: 4 added, 0 changed, 0 destroyed. +``` + +### Environment Teardown + +The test cluster created via the setup-env command can be destroyed using the teardown-env command. + +```sh +$ make teardown-env +... +Destroy complete! Resources: 4 destroyed. +``` + +### Testing Manually + Put the plugin binary into a location of your choice. This directory will be specified as the [`plugin_directory`](https://www.vaultproject.io/docs/configuration#plugin_directory) in the Vault config used to start the server. @@ -112,25 +108,62 @@ $ vault server -dev -config=path/to/config.hcl ... Once the server is started, register the plugin in the Vault server's [plugin catalog](https://www.vaultproject.io/docs/plugins/plugin-architecture#plugin-catalog): ```sh -$ SHA256=$(openssl dgst -sha256 $GOPATH/vault-plugin-secrets-myplugin | cut -d ' ' -f2) -$ vault plugin register \ - -sha256=$SHA256 \ - -command="vault-plugin-secrets-myplugin" \ - secrets myplugin +$ SHA256=$(openssl dgst -sha256 $GOPATH/vault-plugin-database-redis-elasticache | cut -d ' ' -f2) +$ vault plugin register -sha256=$SHA256 database vault-plugin-database-redis-elasticache +... +Success! Data written to: sys/plugins/catalog/database/vault-plugin-database-redis-elasticache +``` + +Enable the database engine to use this plugin: + +```sh +$ vault secrets enable database ... -Success! Data written to: sys/plugins/catalog/myplugin + +Success! Enabled the database secrets engine at: database/ ``` -Enable the secrets engine to use this plugin: +Once the database engine is enabled you can configure an ElastiCache instance: ```sh -$ vault secrets enable myplugin +$ vault write database/config/redis-mydb \ + plugin_name="vault-plugin-database-redis-elasticache" \ + username=$USERNAME \ + password=$PASSWORD \ + url=$URL \ + region=$REGION ... -Successfully enabled 'plugin' at 'myplugin'! +Success! Data written to: database/config/redis-mydb +``` + +Configure a static role: + +```sh +$ vault write database/static-roles/redis-myrole \ + db_name="redis-mydb" \ + username="my-elasticache-username" \ + rotation_period=5m +... + +Success! Data written to: database/roles/redis-myrole +``` + +Retrieve your first set of static credentials: + +```sh +$ vault read database/static-creds/redis-myrole +Key Value +--- ----- +last_vault_rotation 2022-09-06T12:15:33.958413491-04:00 +password PASSWORD +rotation_period 5m +ttl 4m55s +username my-elasticache-username ``` -### Tests + +### Automated Tests To run the tests, invoke `make test`: @@ -144,4 +177,25 @@ You can also specify a `TESTARGS` variable to filter tests like so: $ make test TESTARGS='-run=TestConfig' ``` -[//]: <> (Specify any other test instructions such as acceptance/integration tests) +### Acceptance Tests + +The majority of tests must communicate with an existing ElastiCache instance. See the [Environment Set Up](#environment-set-up) section for instructions on how to prepare a test cluster. + +Some environment variables are required to run tests expecting to communicate with an ElastiCache cluster. +The username and password should be valid IAM access key and secret key with read and write access to the ElastiCache cluster used for testing. The URL should be the complete configuration endpoint including the port, for example: `vault-plugin-elasticache-test.id.xxx.use1.cache.amazonaws.com:6379`. + +```sh +$ export TEST_ELASTICACHE_USERNAME="AWS ACCESS KEY ID" +$ export TEST_ELASTICACHE_PASSWORD="AWS SECRET ACCESS KEY" +$ export TEST_ELASTICACHE_URL="vault-plugin-elasticache-test.id.xxx.use1.cache.amazonaws.com:6379" +$ export TEST_ELASTICACHE_REGION="us-east-1" +$ export TEST_ELASTICACHE_USER="vault-test" + +$ make test +``` + +You can also specify a `TESTARGS` variable to filter tests like so: + +```sh +$ make test TESTARGS='-run=TestConfig' +``` \ No newline at end of file diff --git a/bootstrap/terraform/elasticache.tf b/bootstrap/terraform/elasticache.tf new file mode 100644 index 0000000..c585515 --- /dev/null +++ b/bootstrap/terraform/elasticache.tf @@ -0,0 +1,104 @@ +provider "aws" { + // Credentials and configuration derived from the environment + // Uncomment if you wish to configure the provider explicitly + + // access_key = "" + // secret_key = "" + // region = "" +} + +resource "random_password" "vault_plugin_elasticache_test" { + length = 16 +} + +resource "aws_elasticache_replication_group" "vault_plugin_elasticache_test" { + replication_group_id = "vault-plugin-elasticache-test" + description = "vault elasticache plugin generated test cluster" + engine = "REDIS" + engine_version = "6.2" + node_type = "cache.t4g.micro" + num_cache_clusters = 1 + parameter_group_name = "default.redis6.x" + transit_encryption_enabled = true + user_group_ids = [aws_elasticache_user_group.vault_plugin_elasticache_test.id] + + tags = { + "description" : "vault elasticache plugin generated test cluster" + } +} + +resource "aws_elasticache_user_group" "vault_plugin_elasticache_test" { + engine = "REDIS" + user_group_id = "vault-test-user-group" + user_ids = ["default", aws_elasticache_user.vault_plugin_elasticache_test.user_id] +} + +resource "aws_elasticache_user" "vault_plugin_elasticache_test" { + user_id = "vault-test" + user_name = "vault-test" + access_string = "on ~* +@all" + engine = "REDIS" + passwords = [random_password.vault_plugin_elasticache_test.result] +} + +resource "aws_iam_user" "vault_plugin_elasticache_test" { + name = "vault-plugin-elasticache-user-test" + + tags = { + "description" : "vault elasticache plugin generated test user" + } +} + +resource "aws_iam_access_key" "vault_plugin_elasticache_test" { + user = aws_iam_user.vault_plugin_elasticache_test.name +} + +resource "aws_iam_user_policy" "vault_plugin_elasticache_test" { + name = "vault-plugin-elasticache-policy-test" + user = aws_iam_user.vault_plugin_elasticache_test.name + + policy = data.aws_iam_policy_document.vault_plugin_elasticache_test.json +} + +data "aws_iam_policy_document" "vault_plugin_elasticache_test" { + statement { + actions = [ + "elasticache:DescribeUsers", + "elasticache:ModifyUser", + ] + resources = [ + "arn:aws:elasticache:*.*:user:*", + ] + } +} + +// export TEST_ELASTICACHE_USERNAME=${username} +output "username" { + value = aws_iam_access_key.vault_plugin_elasticache_test.id +} + +// export TEST_ELASTICACHE_PASSWORD=${password} +// Use `terraform output password` to access the value +output "password" { + sensitive = true + value = aws_iam_access_key.vault_plugin_elasticache_test.secret +} + +// export TEST_ELASTICACHE_URL=${url} +output "url" { + value = format( + "%s:%s", + aws_elasticache_replication_group.vault_plugin_elasticache_test.primary_endpoint_address, + aws_elasticache_replication_group.vault_plugin_elasticache_test.port) +} + +// export TEST_ELASTICACHE_REGION=${region} +data "aws_region" "current" {} +output "region" { + value = data.aws_region.current.name +} + +// export TEST_ELASTICACHE_USER=${user} +output "user" { + value = aws_elasticache_user.vault_plugin_elasticache_test.user_name +} diff --git a/cmd/vault-plugin-database-redis-elasticache/main.go b/cmd/vault-plugin-database-redis-elasticache/main.go new file mode 100644 index 0000000..512eac2 --- /dev/null +++ b/cmd/vault-plugin-database-redis-elasticache/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + "os" + + "github.com/hashicorp/vault-plugin-database-redis-elasticache" + "github.com/hashicorp/vault/sdk/database/dbplugin/v5" +) + +func main() { + if err := Run(); err != nil { + log.Println(err) + os.Exit(1) + } +} + +// Run starts serving the plugin +func Run() error { + dbplugin.ServeMultiplex(rediselasticache.New) + + return nil +} diff --git a/cmd/vault-plugin-scaffolding/main.go b/cmd/vault-plugin-scaffolding/main.go deleted file mode 100644 index f3149f4..0000000 --- a/cmd/vault-plugin-scaffolding/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "os" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/sdk/plugin" -) - -func main() { - apiClientMeta := &api.PluginAPIClientMeta{} - flags := apiClientMeta.FlagSet() - flags.Parse(os.Args[1:]) - - tlsConfig := apiClientMeta.GetTLSConfig() - tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig) - - err := plugin.Serve(&plugin.ServeOpts{ - // TODO: Add the plugin's Factory function here, e.g.: - // BackendFactoryFunc: vault-plugin-scaffolding.Factory, - TLSProviderFunc: tlsProviderFunc, - }) - if err != nil { - logger := hclog.New(&hclog.LoggerOptions{}) - - logger.Error("plugin shutting down", "error", err) - os.Exit(1) - } -} diff --git a/go.mod b/go.mod index e85d5fc..ff7530b 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ -module github.com/hashicorp/vault-plugin-scaffolding +module github.com/hashicorp/vault-plugin-database-redis-elasticache go 1.17 require ( + github.com/aws/aws-sdk-go v1.44.81 github.com/hashicorp/go-hclog v1.3.0 - github.com/hashicorp/vault/api v1.7.2 + github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 github.com/hashicorp/vault/sdk v0.5.3 + github.com/mitchellh/mapstructure v1.5.0 ) require ( github.com/armon/go-metrics v0.3.9 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/frankban/quicktest v1.14.2 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -22,36 +22,25 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.3 // indirect - github.com/hashicorp/go-retryablehttp v0.6.6 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/go-version v1.2.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/grpc v1.41.0 // indirect google.golang.org/protobuf v1.26.0 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index feb3413..5cd3d02 100644 --- a/go.sum +++ b/go.sum @@ -10,14 +10,14 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.44.81 h1:C8oBZ+a+ka0qk3Q24MohQIFq0tkbO8IAu5tfpAMKVWE= +github.com/aws/aws-sdk-go v1.44.81/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= @@ -36,7 +36,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -44,7 +43,6 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns= github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -54,8 +52,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -93,10 +91,8 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= @@ -104,7 +100,6 @@ github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy v0.1.0 h1:xuTi5ZwjimfpvpL09jDE71smCBRpnF5xfo871BSX4gs= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -112,22 +107,17 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= +github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= @@ -137,11 +127,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ= -github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M= -github.com/hashicorp/vault/sdk v0.5.1/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= @@ -149,6 +135,11 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -156,7 +147,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -177,10 +167,7 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -188,7 +175,6 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -226,7 +212,6 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -242,7 +227,6 @@ github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -263,10 +247,11 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -287,21 +272,20 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -345,13 +329,14 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugin.go b/plugin.go new file mode 100644 index 0000000..0708f40 --- /dev/null +++ b/plugin.go @@ -0,0 +1,33 @@ +package rediselasticache + +import ( + "os" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/sdk/database/dbplugin/v5" +) + +func New() (interface{}, error) { + logger := hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: true, + }) + + db := &redisElastiCacheDB{ + logger: logger, + } + + return wrapWithSanitizerMiddleware(db), nil +} + +func wrapWithSanitizerMiddleware(db *redisElastiCacheDB) dbplugin.Database { + return dbplugin.NewDatabaseErrorSanitizerMiddleware(db, db.secretValuesToMask) +} + +func (r *redisElastiCacheDB) secretValuesToMask() map[string]string { + return map[string]string{ + r.config.Password: "[password]", + r.config.Username: "[username]", + } +} diff --git a/redis_elasticache_client.go b/redis_elasticache_client.go new file mode 100644 index 0000000..823b8a1 --- /dev/null +++ b/redis_elasticache_client.go @@ -0,0 +1,110 @@ +package rediselasticache + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-secure-stdlib/awsutil" + "github.com/hashicorp/vault/sdk/database/dbplugin/v5" + "github.com/mitchellh/mapstructure" +) + +// Verify interface is implemented +var _ dbplugin.Database = (*redisElastiCacheDB)(nil) + +type redisElastiCacheDB struct { + logger hclog.Logger + config config + client *elasticache.ElastiCache +} + +type config struct { + Username string `mapstructure:"username,omitempty"` + Password string `mapstructure:"password,omitempty"` + Url string `mapstructure:"url,omitempty"` + Region string `mapstructure:"region,omitempty"` +} + +func (r *redisElastiCacheDB) Initialize(_ context.Context, req dbplugin.InitializeRequest) (dbplugin.InitializeResponse, error) { + r.logger.Debug("initializing AWS ElastiCache Redis client") + + if err := mapstructure.WeakDecode(req.Config, &r.config); err != nil { + return dbplugin.InitializeResponse{}, err + } + + creds, err := awsutil.RetrieveCreds(r.config.Username, r.config.Password, "", r.logger) + if err != nil { + return dbplugin.InitializeResponse{}, fmt.Errorf("unable to rerieve AWS credentials from provider chain: %w", err) + } + + region, err := awsutil.GetRegion(r.config.Region) + if err != nil { + return dbplugin.InitializeResponse{}, fmt.Errorf("unable to determine AWS region from config nor context: %w", err) + } + + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(region), + Credentials: creds, + }) + if err != nil { + return dbplugin.InitializeResponse{}, fmt.Errorf("unable to initialize AWS session: %w", err) + } + r.client = elasticache.New(sess) + + if req.VerifyConnection { + r.logger.Debug("Verifying connection to instance", "url", r.config.Url) + + _, err := r.client.DescribeUsers(nil) + if err != nil { + return dbplugin.InitializeResponse{}, fmt.Errorf("unable to connect to ElastiCache Redis endpoint: %w", err) + } + } + + return dbplugin.InitializeResponse{ + Config: req.Config, + }, nil +} + +func (r *redisElastiCacheDB) Type() (string, error) { + return "redisElastiCache", nil +} + +func (r *redisElastiCacheDB) Close() error { + return nil +} + +func (r *redisElastiCacheDB) NewUser(_ context.Context, _ dbplugin.NewUserRequest) (dbplugin.NewUserResponse, error) { + return dbplugin.NewUserResponse{}, fmt.Errorf("user creation not supported") +} + +func (r *redisElastiCacheDB) DeleteUser(_ context.Context, _ dbplugin.DeleteUserRequest) (dbplugin.DeleteUserResponse, error) { + return dbplugin.DeleteUserResponse{}, fmt.Errorf("user deletion not supported") +} + +func (r *redisElastiCacheDB) UpdateUser(_ context.Context, req dbplugin.UpdateUserRequest) (dbplugin.UpdateUserResponse, error) { + r.logger.Debug("updating AWS ElastiCache Redis user", "username", req.Username) + + out, err := r.client.DescribeUsers(&elasticache.DescribeUsersInput{ + UserId: aws.String(req.Username), + }) + if err != nil { + return dbplugin.UpdateUserResponse{}, fmt.Errorf("unable to get user %s: %w", req.Username, err) + } + if len(out.Users) == 1 && *out.Users[0].Status != "active" { + return dbplugin.UpdateUserResponse{}, fmt.Errorf("user %s cannot be updated because it is not in the 'active' state", req.Username) + } + + _, err = r.client.ModifyUser(&elasticache.ModifyUserInput{ + UserId: &req.Username, + Passwords: []*string{&req.Password.NewPassword}, + }) + if err != nil { + return dbplugin.UpdateUserResponse{}, fmt.Errorf("unable to update user %s: %w", req.Username, err) + } + + return dbplugin.UpdateUserResponse{}, nil +} diff --git a/redis_elasticache_client_test.go b/redis_elasticache_client_test.go new file mode 100644 index 0000000..5cc9828 --- /dev/null +++ b/redis_elasticache_client_test.go @@ -0,0 +1,205 @@ +package rediselasticache + +import ( + "context" + "os" + "reflect" + "testing" + + "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/sdk/database/dbplugin/v5" +) + +type fields struct { + logger hclog.Logger + config config + client *elasticache.ElastiCache +} + +type args struct { + ctx context.Context + req interface{} +} + +type testCases []struct { + name string + fields fields + args args + want interface{} + wantErr bool +} + +func skipIfEnvIsUnset(t *testing.T, config config, username string) { + if config.Username == "" || config.Password == "" || config.Url == "" || config.Region == "" || username == "" { + t.Skip("Skipping acceptance tests because required environment variables are not configured") + } +} + +func setUpEnvironment() (fields, map[string]interface{}, redisElastiCacheDB, string) { + username := os.Getenv("TEST_ELASTICACHE_USERNAME") + password := os.Getenv("TEST_ELASTICACHE_PASSWORD") + url := os.Getenv("TEST_ELASTICACHE_URL") + region := os.Getenv("TEST_ELASTICACHE_REGION") + user := os.Getenv("TEST_ELASTICACHE_USER") + + f := fields{ + logger: hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: true, + }), + config: config{ + Username: username, + Password: password, + Url: url, + Region: region, + }, + client: nil, + } + + c := map[string]interface{}{ + "username": username, + "password": password, + "url": url, + "region": region, + } + + r := redisElastiCacheDB{ + logger: f.logger, + config: f.config, + client: f.client, + } + + return f, c, r, user +} + +func setUpClient(t *testing.T, r *redisElastiCacheDB, config map[string]interface{}) { + _, err := r.Initialize(nil, dbplugin.InitializeRequest{ + Config: config, + VerifyConnection: true, + }) + if err != nil { + t.Errorf("unable to pre initialize redis client for test cases: %v", err) + } +} + +func Test_redisElastiCacheDB_Initialize(t *testing.T) { + f, c, r, u := setUpEnvironment() + skipIfEnvIsUnset(t, f.config, u) + + tests := testCases{ + { + name: "initialize and verify connection succeeds", + fields: f, + args: args{ + req: dbplugin.InitializeRequest{ + Config: c, + VerifyConnection: true, + }, + }, + want: dbplugin.InitializeResponse{ + Config: c, + }, + }, + { + name: "initialize with invalid config fails", + fields: f, + args: args{ + req: dbplugin.InitializeRequest{ + Config: map[string]interface{}{ + "username": "wrong", + "password": "wrong", + "url": "wrong", + "region": "wrong", + }, + VerifyConnection: true, + }, + }, + want: dbplugin.InitializeResponse{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &r + got, err := r.Initialize(tt.args.ctx, tt.args.req.(dbplugin.InitializeRequest)) + if (err != nil) != tt.wantErr { + t.Errorf("Initialize() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Initialize() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_redisElastiCacheDB_UpdateUser(t *testing.T) { + f, c, r, u := setUpEnvironment() + + skipIfEnvIsUnset(t, f.config, u) + setUpClient(t, &r, c) + + tests := testCases{ + { + name: "update password of existing user succeeds", + fields: f, + args: args{ + ctx: context.Background(), + req: dbplugin.UpdateUserRequest{ + Username: u, + CredentialType: 0, + Password: &dbplugin.ChangePassword{ + NewPassword: "abcdefghijklmnopqrstuvwxyz1", + }, + }, + }, + want: dbplugin.UpdateUserResponse{}, + }, + { + name: "update password of non-existing user fails", + fields: f, + args: args{ + ctx: context.Background(), + req: dbplugin.UpdateUserRequest{ + Username: "I do not exist", + CredentialType: 0, + Password: &dbplugin.ChangePassword{ + NewPassword: "abcdefghijklmnopqrstuvwxyz1", + }, + }, + }, + want: dbplugin.UpdateUserResponse{}, + wantErr: true, + }, + { + name: "update to invalid password fails", + fields: f, + args: args{ + ctx: context.Background(), + req: dbplugin.UpdateUserRequest{ + Username: u, + CredentialType: 0, + Password: &dbplugin.ChangePassword{ + NewPassword: "too short", + }, + }, + }, + want: dbplugin.UpdateUserResponse{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := r.UpdateUser(tt.args.ctx, tt.args.req.(dbplugin.UpdateUserRequest)) + if (err != nil) != tt.wantErr { + t.Errorf("UpdateUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("UpdateUser() got = %v, want %v", got, tt.want) + } + }) + } +}