Compare commits

..
Author SHA1 Message Date
Danielle Miu dac83e23b6 add DeleteObjects command to plugin 2023-12-15 19:03:23 +01:00
dependabot[bot] 86d383c7dd build(deps): bump google.golang.org/grpc from 1.53.0 to 1.56.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.56.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.56.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-22 10:02:47 +02:00
dependabot[bot] 5ba86d5b84 build(deps): bump golang.org/x/net from 0.9.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-22 09:56:51 +02:00
Hugo 39f8f290fc fix(credential): Allow for static AWS credentials in environment
This commit fixes a problem where the plugin/AWS SDK would not recognise
static AWS credentials set in the machine's environment variables (by
setting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)

The default `WithSharedCredentials` value is `true` in awsutil (v2) -
This essentially disables the ability to read credentials from the
environment because we set the `default` AWS profile options, making the
SDK go search for a profile that may not exist and ignoring the
environment in the way, so we need to set it to `false` (see
`awsutil@v2.(*CredentialsConfig).generateAwsConfigOptions`) for extra
details.
2023-10-05 14:47:16 +01:00
Damian DebkowskiandHugo fcfe267a3f refactor: switch from awsutil to awsutilv2 (#34)
* fix(storage): Storage Assume Role Support

This commit introduces fixes to enable operators to use AWS dynamic
credentials on the storage side of this plugin, namely validation,
improved credential lifecycle management to allow for storage buckets to
be updated from static to dynamic credentials and vice versa, and a new
way to determine credential types.

It also introduces new tests for dynamic credential usage for the
various functions.

* fix(storage): Static rotated creds being deleted on plugin err

On Storage Bucket update, when we determine if the credentials are to be
updated to new ones, we call `credState.ReplaceCreds`.

This call, if the old credentials were managed by us (rotated at create
time), will delete the old credentials and set the new ones onto
credState.

We called this function without validating if the incoming credentials
are valid. This could get the bucket in a state where where, while we'd
return an update error if the incoming credentials weren't working with
S3 (err in the dry run process that happens at the end of the update),
we'd also silently delete the old credentials the bucket used, rendering
the bucket completely broken.

---------

Co-authored-by: Hugo <hugoamvieira@users.noreply.github.com>
2023-09-22 12:26:08 -07:00
Michael Li f263eefbba doc: Fix links to READMEs (#32) 2023-09-08 14:31:18 -04:00
19 changed files with 2174 additions and 584 deletions

No files matched your search

+4 -4
View File
@@ -17,7 +17,7 @@ and then the given credential will be revoked. In this way, after rotation,
only Boundary knows the client secret in use by this plugin. More information only Boundary knows the client secret in use by this plugin. More information
about AWS static credentials can be found [here](https://docs.aws.amazon.com/sdkref/latest/guide/feature-static-credentials.html). about AWS static credentials can be found [here](https://docs.aws.amazon.com/sdkref/latest/guide/feature-static-credentials.html).
Credential rotation can be turned off by setting the Credential rotation can be turned off by setting the
`disable_credential_rotation` attribute to `true`. `disable_credential_rotation` attribute to `true`.
### Assume Role Credentials ### Assume Role Credentials
@@ -48,7 +48,7 @@ The plugin fetches hosts through the
[DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) [DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html)
call. call.
[Getting Started](https://github.com/hashicorp/boundary-plugin-aws/plugin/service/host/README.md) [Getting Started](plugin/service/host/README.md)
## Storage Bucket ## Storage Bucket
@@ -60,7 +60,7 @@ these storage bucket resources can in turn be associated to targets within
Boundary. Boundary.
At creation, update or deletion of a storage bucket of this type, configuration of the At creation, update or deletion of a storage bucket of this type, configuration of the
plugin is performed via the attribute/secret values passed to the create, update, or plugin is performed via the attribute/secret values passed to the create, update, or
delete calls actions. The values passed in to the plugin here are the attributes set delete calls actions. The values passed in to the plugin here are the attributes set
on on a storage bucket in boundary. on on a storage bucket in boundary.
@@ -76,4 +76,4 @@ The plugin fetches metadata about the files through the
[HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) [HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html)
call. call.
[Getting Started](https://github.com/hashicorp/boundary-plugin-aws/plugin/service/storage/README.md) [Getting Started](plugin/service/storage/README.md)
+37 -37
View File
@@ -1,65 +1,65 @@
module github.com/hashicorp/boundary-plugin-aws module github.com/hashicorp/boundary-plugin-aws
go 1.20 go 1.21
require ( require (
github.com/aws/aws-sdk-go v1.44.80 github.com/aws/aws-sdk-go v1.44.80
github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2 v1.20.1
github.com/aws/aws-sdk-go-v2/config v1.18.19
github.com/aws/aws-sdk-go-v2/credentials v1.13.18
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0 github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 github.com/aws/aws-sdk-go-v2/service/iam v1.22.2
github.com/aws/smithy-go v1.13.5 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2
github.com/aws/smithy-go v1.14.1
github.com/google/go-cmp v0.5.9 github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.1
github.com/hashicorp/boundary/sdk v0.0.33 github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2 github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0
github.com/hashicorp/terraform-json v0.14.0 github.com/hashicorp/terraform-json v0.14.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.53.0 google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.28.1 google.golang.org/protobuf v1.31.0
) )
require ( require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.14.1 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24 // indirect github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 // indirect
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24 // indirect github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9 // indirect github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.5.0 // indirect github.com/hashicorp/go-version v1.5.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect github.com/zclconf/go-cty v1.10.0 // indirect
golang.org/x/crypto v0.8.0 // indirect golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.9.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.13.0 // indirect
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
+114 -94
View File
@@ -1,132 +1,153 @@
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI= github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI=
github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY=
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= github.com/aws/aws-sdk-go-v2 v1.20.1 h1:rZBf5DWr7YGrnlTK4kgDQGn1ltqOg5orCYb/UhOFZkg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 h1:lN6L3LrYHeZ6xCxaIYtoWCx4GMLk4nRknsh29OMSqHY=
github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12/go.mod h1:TDCkEAkMTXxTs0oLBGBKpBZbk3NLh8EvAfF0Q3x8/0c=
github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c= github.com/aws/aws-sdk-go-v2/config v1.18.33 h1:JKcw5SFxFW/rpM4mOPjv0VQ11E2kxW13F3exWOy7VZU=
github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= github.com/aws/aws-sdk-go-v2/config v1.18.33/go.mod h1:hXO/l9pgY3K5oZJldamP0pbZHdPqqk+4/maa7DSD3cA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= github.com/aws/aws-sdk-go-v2/credentials v1.13.32 h1:lIH1eKPcCY1ylR4B6PkBGRWMHO3aVenOKJHWiS4/G2w=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= github.com/aws/aws-sdk-go-v2/credentials v1.13.32/go.mod h1:lL8U3v/Y79YRG69WlAho0OHIKUXCyFvSXaIvfo81sls=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 h1:DK/9C+UN/X+1+Wm8pqaDksQr2tSLzq+8X1/rI/ZxKEQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8/go.mod h1:ce7BgLQfYr5hQFdy67oX2svto3ufGtm6oBvmsHScI1Q=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 h1:c8ed/T9T2K5I+h/JzmF5tpI46+OODQ74dzmdo+QnaMg=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38/go.mod h1:qggunOChCMu9ZF/UkAfhTz25+U2rLVb3ya0Ua6TTfCA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 h1:hNeAAymUY5gu11WrrmFb3CVIp9Dar9hbo44yzzcQpzA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32/go.mod h1:0ZXSqrty4FtQ7p8TEuRde/SZm9X05KT18LAUlR40Ln0=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 h1:fc0ukRAiP1syoSGZYu+DaE+FulSYhTiJ8WpVu5jElU4=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39/go.mod h1:WLAW8PT7+JhjZfLSWe7WEJaJu0GNo0cKc2Zyo003RBs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 h1:vUh7dBFNS3oFCtVv6CiYKh5hP9ls8+kIpKLeFruIBLk=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1/go.mod h1:sFMeinkhj/SZKQM8BxtvNtSPjJEo0Xrz+w3g2e4FSKI=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0 h1:NXi4pNJWjAaiI56P1Rl8DC9A4jMNRE00WNBsDua5WRg= github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0 h1:NXi4pNJWjAaiI56P1Rl8DC9A4jMNRE00WNBsDua5WRg=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0/go.mod h1:L3ZT0N/vBsw77mOAawXmRnREpEjcHd2v5Hzf7AkIH8M= github.com/aws/aws-sdk-go-v2/service/ec2 v1.99.0/go.mod h1:L3ZT0N/vBsw77mOAawXmRnREpEjcHd2v5Hzf7AkIH8M=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/iam v1.22.2 h1:DPFxx/6Zwes/MiadlDteVqDKov7yQ5v9vuwfhZuJm1s=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= github.com/aws/aws-sdk-go-v2/service/iam v1.22.2/go.mod h1:cQTMNdo/Z5t1DDRsUnx0a2j6cPnytMBidUYZw2zks28=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 h1:iV/W5OMBys+66OeXJi/7xIRrKZNsu0ylsLGu+6nbmQE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13/go.mod h1:ReJb6xYmtGyu9KoFtRreWegbN9dZqvZIIv4vWnhcsyI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 h1:QviNkc+vGSuEHx8P+pVNKOdWLXBPIwMFv7p0fphgE4U=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33/go.mod h1:fABTUmOrAgAalG2i9WJpjBvlnk7UK8YmnYaxN+Q2CwE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 h1:dGAseBFEYxth10V23b5e2mAS+tX7oVbfYHD6dnDdAsg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32/go.mod h1:4jwAWKEkCR0anWk5+1RbfSg1R5Gzld7NLiuaq5bTR/Y=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 h1:PJH4I+qYjPXclKRbVCW47iYUvtXEh1u6YmDhn5J8VQE= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 h1:PT6PBCycRwhpEW5hJnRiceCeoWJ+r3bdgXtV+VKG7Pk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1/go.mod h1:TqoxCLwT2nrxrBGA+z7t6OWM7LBkgRckK3gOjYE+7JA=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2 h1:v346f1h8sUBKXnEbrv43L37MTBlFHyKXQPIZHNAaghA=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2/go.mod h1:cwCATiyNrXK9P2FsWdZ89g9mpsYv2rhk0UA/KByl5fY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4= github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 h1:A2RlEMo4SJSwbNoUUgkxTAEMduAy/8wG3eB2b2lP4gY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= github.com/aws/aws-sdk-go-v2/service/sso v1.13.2/go.mod h1:ju+nNXUunfIFamXUIZQiICjnO/TPlOmWcYhZcSy7xaE=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 h1:OJELEgyaT2kmaBGZ+myyZbTTLobfe3ox3FSh5eYK9Qs=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2/go.mod h1:ubDBBaDFs1GHijSOTi8ljppML15GLG0HxhILtbjNNYQ=
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/aws-sdk-go-v2/service/sts v1.21.2 h1:ympg1+Lnq33XLhcK/xTG4yZHPs1Oyxu+6DEWbl7qOzA=
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2/go.mod h1:FQ/DQcOfESELfJi5ED+IPPAjI5xC6nxtSolVVB773jM=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs=
github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/boundary/sdk v0.0.33 h1:kHaVZ3b9j1QvYlG+vhAgrbXh8/+Rq2qlUIs8fKMQH7k= github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e h1:PzZklmCjnaqQq9JdZRI9SVPSd9xo0iXLlmOHoQX68aM=
github.com/hashicorp/boundary/sdk v0.0.33/go.mod h1:jn9j5mM8v2pOk8aLeJNIszdm7WBf4gRSP68iy8Iu5Q0= github.com/hashicorp/boundary/sdk v0.0.41-0.20231117205527-08045ddb050e/go.mod h1:+XTDYf9YNeKIbGOPJwy7hlO2Le4zgzCtHCG/u+z4THI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 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/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24 h1:vtCJgcxwDMFrpUgVMfjtwXY1YqvHbj8pNA7GsXYEoLg= github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 h1:f9oX8/3zxiQrfrWnBeyjDm4S02GAU02OBtCRoZOUwlo=
github.com/hashicorp/eventlogger v0.1.2-0.20230428153751-cca445805f24/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM= github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM=
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24 h1:mopKPTxSaVr24uSysyRwywTDs85L5b05qqZvEkWa0qc= github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 h1:iAb287bq0TaWTnhDYuN/zVqdD2EwanQg9ncVelC60Xc=
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20230428153751-cca445805f24/go.mod h1:EQPLoX6CONA9BSYUovTQBHfPGE91g7wOxv03sO29FzY= github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0/go.mod h1:tMywUTIvdB/FXhwm6HMTt61C8/eODY6gitCHhXtyojg=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 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-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.4 h1:8z+Zi0uLYXONkxwWb/D7O5t1IUucB6Ft3uvSOa950bM= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 h1:jrnDfQm2hCQ0/hEselgqzV4fK16gpZoY0OWGZpVPNHM=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9 h1:JpCvi97NMA+saNqO8ovQcGoRbBq6P5ZZlJqvOsW5ick= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5/go.mod h1:psh1qKep5ukvuNobFY/hCybuudlkkACpmazOsCgX5Rg=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9/go.mod h1:NtMaPhqSlfQ72XWDD2g80o8HI8RKkowIB8/WZHMyPY4= github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 h1:1ZuhfnZgRnLK8S0KovJkoTCRIQId5pv3sDR7pG5VQBw=
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14/go.mod h1:0dWtzl2ilqKpavgM3id/kFK9L3tjo6fS4OhbVPSYpnQ=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.4.9 h1:ESiK220/qE0aGxWdzKIvRH69iLiuN/PjoLTm69RoWtU= github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2 h1:kWg2vyKl7BRXrNxYziqDJ55n+vtOQ1QsGORjzoeB+uM= github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.2/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg= github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0 h1:ca5TSI4AgaOncPpyzLDtCGjVEtKukONpeM95vFxXCOQ=
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0/go.mod h1:7CUvZtfTp2U0CYQCLzMtS2ngckjAZePSfwrE2aeDP1M=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.9 h1:YZ01/aiARPneDUryzjiPGvr3pmjCSC+1WBZKI1P7I7k= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.6 h1:rOmnqOSUBn5opme1Kh9OXO8qWd0KztO+f02vvCwCc/w= github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 h1:uPW2Wn0YlmI9RGSkZpcIplnVRwJ7BCiGpk1vnF2TMw4=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11/go.mod h1:uis9dCmOzXuOaRyXq+1Foh31kcvXKoWogjNnhfjHfW8=
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.4 h1:IU2iGcvthrJ53rPbQU6B4+iVs/cLPO89ti5dd2XEj3k= github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9 h1:0S0ctJ7Ra8O7ap+/3fZUnzJ3VzJyirWS/WnNCuOYtZY=
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9/go.mod h1:TNNdgtjLgVDbrgFcyCKrlAicIl3dZF94swJltyGUX2M=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6 h1:ZYv2XA+tEfFXIToR2jmBgVqQU9gERt0APbWqmUoNGnY=
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6/go.mod h1:ggFN8dlaLWS2R1gymBbCrvXM/bkZP7hEAa4seqDwhyg=
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI= github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= 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 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 h1:phcbL8urUzF/kxA/Oj6awENaRwfWsjP59GW7u2qlDyY= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 h1:xbrxd0U9XQW8qL1BAz2XrAjAF/P2vcqUTAues9c24B8=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3/go.mod h1:LWq2Sy8UoKKuK4lFuCNWSjJj57MhNNf2zzBWMtkAIX4=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E=
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 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/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s=
github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4= github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 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 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 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -139,12 +160,14 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -153,31 +176,28 @@ github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= 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/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
@@ -186,16 +206,16 @@ github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q
github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -204,12 +224,11 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -224,41 +243,42 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 h1:QQF+HdiI4iocoxUjjpLgvTYDHKm99C/VtTBFnfiCJos= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+32 -12
View File
@@ -9,7 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/hashicorp/boundary-plugin-aws/internal/errors" "github.com/hashicorp/boundary-plugin-aws/internal/errors"
"github.com/hashicorp/boundary-plugin-aws/internal/values" "github.com/hashicorp/boundary-plugin-aws/internal/values"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
@@ -68,25 +68,45 @@ func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes,
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field" badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
} }
if len(badFields) > 0 { opts := []awsutil.Option{}
return nil, errors.InvalidArgumentError("Error in the secrets provided", badFields) // logic for parsing the credential type
} // supported types:
// - user static credential
opts := []awsutil.Option{ // - ec2 assume role dynamic credential
awsutil.WithEnvironmentCredentials(true), // - enviornment variables credential
} switch {
if accessKey != "" && secretKey != "" { // static credentials and dynamic credentials cannot be used together
case accessKey != "" && secretKey != "" && attrs.RoleArn != "":
badFields[fmt.Sprintf("secrets.%s", ConstAccessKeyId)] = "conflicts with role_arn value"
badFields[fmt.Sprintf("secrets.%s", ConstSecretAccessKey)] = "conflicts with role_arn value"
badFields[fmt.Sprintf("attributes.%s", ConstRoleArn)] = "conflicts with access_key_id and secret_access_key values"
// static credential is missing it's secret_access_key
case accessKey != "" && secretKey == "":
badFields[fmt.Sprintf("secrets.%s", ConstSecretAccessKey)] = "missing required value"
// static credential is missing it's access_key_id
case accessKey == "" && secretKey != "":
badFields[fmt.Sprintf("secrets.%s", ConstAccessKeyId)] = "missing required value"
// dynamic credentials and credential rotation is not supported
case len(attrs.RoleArn) > 0 && !attrs.DisableCredentialRotation:
badFields[fmt.Sprintf("attributes.%s", ConstDisableCredentialRotation)] = "disable_credential_rotation attribute is required when providing a role_arn"
// add static credentials
case accessKey != "" && secretKey != "":
opts = append(opts, opts = append(opts,
awsutil.WithAccessKey(accessKey), awsutil.WithAccessKey(accessKey),
awsutil.WithSecretKey(secretKey), awsutil.WithSecretKey(secretKey),
) )
// add dynamic credentials
case attrs.RoleArn != "":
opts = append(opts, awsutil.WithRoleArn(attrs.RoleArn))
} }
if len(badFields) > 0 {
return nil, errors.InvalidArgumentError("Error in the secrets provided", badFields)
}
if attrs.Region != "" { if attrs.Region != "" {
opts = append(opts, awsutil.WithRegion(attrs.Region)) opts = append(opts, awsutil.WithRegion(attrs.Region))
} }
if attrs.RoleArn != "" {
opts = append(opts, awsutil.WithRoleArn(attrs.RoleArn))
}
if attrs.RoleExternalId != "" { if attrs.RoleExternalId != "" {
opts = append(opts, awsutil.WithRoleExternalId(attrs.RoleExternalId)) opts = append(opts, awsutil.WithRoleExternalId(attrs.RoleExternalId))
} }
+18 -13
View File
@@ -6,7 +6,7 @@ package credential
import ( import (
"testing" "testing"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -118,7 +118,7 @@ func TestGetCredentialsConfig(t *testing.T) {
expectedErrContains string expectedErrContains string
}{ }{
{ {
name: "no secrets", name: "no credentials",
attrs: &CredentialAttributes{ attrs: &CredentialAttributes{
Region: "us-west-2", Region: "us-west-2",
}, },
@@ -130,7 +130,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name: "with static credentials", name: "with static credentials",
secrets: &structpb.Struct{ secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{ Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"), ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"), ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}, },
}, },
@@ -138,7 +138,7 @@ func TestGetCredentialsConfig(t *testing.T) {
Region: "us-west-2", Region: "us-west-2",
}, },
expected: &awsutil.CredentialsConfig{ expected: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
Region: "us-west-2", Region: "us-west-2",
}, },
@@ -160,7 +160,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name: "missing secret key", name: "missing secret key",
secrets: &structpb.Struct{ secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{ Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"), ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
}, },
}, },
required: true, required: true,
@@ -173,7 +173,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name: "unknown fields", name: "unknown fields",
secrets: &structpb.Struct{ secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{ Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"), ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"), ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
"foo": structpb.NewBoolValue(true), "foo": structpb.NewBoolValue(true),
"bar": structpb.NewBoolValue(true), "bar": structpb.NewBoolValue(true),
@@ -188,7 +188,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name: "valid ignore creds_last_rotated_time", name: "valid ignore creds_last_rotated_time",
secrets: &structpb.Struct{ secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{ Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"), ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"), ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"), ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
}, },
@@ -197,7 +197,7 @@ func TestGetCredentialsConfig(t *testing.T) {
Region: "us-west-2", Region: "us-west-2",
}, },
expected: &awsutil.CredentialsConfig{ expected: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
Region: "us-west-2", Region: "us-west-2",
}, },
@@ -215,6 +215,7 @@ func TestGetCredentialsConfig(t *testing.T) {
RoleTags: map[string]string{ RoleTags: map[string]string{
"foo": "bar", "foo": "bar",
}, },
DisableCredentialRotation: true,
}, },
expected: &awsutil.CredentialsConfig{ expected: &awsutil.CredentialsConfig{
Region: "us-west-2", Region: "us-west-2",
@@ -230,7 +231,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name: "with static credential & assume role", name: "with static credential & assume role",
secrets: &structpb.Struct{ secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{ Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"), ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"), ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
}, },
}, },
@@ -243,17 +244,21 @@ func TestGetCredentialsConfig(t *testing.T) {
"foo": "bar", "foo": "bar",
}, },
}, },
expected: &awsutil.CredentialsConfig{ expectedErrContains: "attributes.role_arn: conflicts with access_key_id and secret_access_key values, secrets.access_key_id: conflicts with role_arn value, secrets.secret_access_key: conflicts with role_arn value",
AccessKey: "foobar", },
SecretKey: "bazqux", {
name: "with dynamic credentials and no disable credential rotation",
secrets: &structpb.Struct{Fields: map[string]*structpb.Value{}},
attrs: &CredentialAttributes{
Region: "us-west-2", Region: "us-west-2",
RoleARN: "arn:aws:iam::123456789012:role/S3Access", RoleArn: "arn:aws:iam::123456789012:role/S3Access",
RoleExternalId: "1234567890", RoleExternalId: "1234567890",
RoleSessionName: "test-session", RoleSessionName: "test-session",
RoleTags: map[string]string{ RoleTags: map[string]string{
"foo": "bar", "foo": "bar",
}, },
}, },
expectedErrContains: "attributes.disable_credential_rotation: disable_credential_rotation attribute is required when providing a role_arn",
}, },
} }
+84 -41
View File
@@ -4,19 +4,41 @@
package credential package credential
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go/aws/session" iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/boundary-plugin-aws/internal/values" "github.com/hashicorp/boundary-plugin-aws/internal/values"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
type CredentialType int
const (
// StaticAWS denotes an Access Key Id that begins with "AKIA". These are
// long-term access keys, provided by AWS, for an IAM user or an AWS account
// root user.
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys
StaticAWS CredentialType = iota
// DynamicAWS denotes the presence of a RoleARN, or an Access Key Id that
// begins with "ASIA". The latter are temporary credentials access keys that
// are created using AWS STS operations.
DynamicAWS
// StaticOther denotes the presence of an Access Key Id that does not follow
// the AKIA/ASIA convention outlined by AWS.
StaticOther
// Unknown is a catch-all for everything else.
Unknown
)
// rotationWaitTimeout controls the time we wait for credential rotation to // rotationWaitTimeout controls the time we wait for credential rotation to
// succeed. This is important to ensure that rotated credentials can be used // succeed. This is important to ensure that rotated credentials can be used
// right away. // right away.
@@ -85,11 +107,11 @@ func NewAwsCredentialPersistedState(opts ...AwsCredentialPersistedStateOption) (
// ValidateCreds takes the credentials configuration from the persisted state // ValidateCreds takes the credentials configuration from the persisted state
// and runs sts.GetCallerIdentity for the current credentials, which is done // and runs sts.GetCallerIdentity for the current credentials, which is done
// to check that the credentials are valid. // to check that the credentials are valid.
func (s *AwsCredentialPersistedState) ValidateCreds() error { func (s *AwsCredentialPersistedState) ValidateCreds(ctx context.Context) error {
if s.CredentialsConfig == nil { if s.CredentialsConfig == nil {
return errors.New("missing credentials config") return errors.New("missing credentials config")
} }
if _, err := s.CredentialsConfig.GetCallerIdentity(s.testOpts...); err != nil { if _, err := s.CredentialsConfig.GetCallerIdentity(ctx, s.testOpts...); err != nil {
return fmt.Errorf("error validating credentials: %w", err) return fmt.Errorf("error validating credentials: %w", err)
} }
return nil return nil
@@ -100,11 +122,14 @@ func (s *AwsCredentialPersistedState) ValidateCreds() error {
// written into the credentials config and the persisted state. On any error, the old credentials are not overwritten. // written into the credentials config and the persisted state. On any error, the old credentials are not overwritten.
// This ensures that any generated new secret key never leaves this function in case of an error, even though it will // This ensures that any generated new secret key never leaves this function in case of an error, even though it will
// still result in an extraneous access key existing. // still result in an extraneous access key existing.
func (s *AwsCredentialPersistedState) RotateCreds() error { func (s *AwsCredentialPersistedState) RotateCreds(ctx context.Context) error {
if s.CredentialsConfig == nil { if s.CredentialsConfig == nil {
return errors.New("missing credentials config") return errors.New("missing credentials config")
} }
if err := s.CredentialsConfig.RotateKeys(append([]awsutil.Option{ if GetCredentialType(s.CredentialsConfig) != StaticAWS {
return errors.New("invalid credential type")
}
if err := s.CredentialsConfig.RotateKeys(ctx, append([]awsutil.Option{
awsutil.WithValidityCheckTimeout(rotationWaitTimeout), awsutil.WithValidityCheckTimeout(rotationWaitTimeout),
}, s.testOpts...)...); err != nil { }, s.testOpts...)...); err != nil {
return fmt.Errorf("error rotating credentials: %w", err) return fmt.Errorf("error rotating credentials: %w", err)
@@ -116,7 +141,7 @@ func (s *AwsCredentialPersistedState) RotateCreds() error {
// ReplaceCreds replaces the access key in the state with a new key. // ReplaceCreds replaces the access key in the state with a new key.
// If the existing key was rotated at any point in time, it is // If the existing key was rotated at any point in time, it is
// deleted first, otherwise it's left alone. // deleted first, otherwise it's left alone.
func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.CredentialsConfig) error { func (s *AwsCredentialPersistedState) ReplaceCreds(ctx context.Context, credentialsConfig *awsutil.CredentialsConfig) error {
if credentialsConfig == nil { if credentialsConfig == nil {
return errors.New("missing new credentials config") return errors.New("missing new credentials config")
} }
@@ -124,11 +149,11 @@ func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.Cr
return errors.New("missing credentials config") return errors.New("missing credentials config")
} }
if !s.CredsLastRotatedTime.IsZero() { // Delete old/existing credentials. This action is only possible for static credential types.
// Delete old/existing credentials. This is done with the same // This is done with the same credentials to ensure that it has the proper permissions to do it.
// credentials to ensure that it has the proper permissions to do ct := GetCredentialType(s.CredentialsConfig)
// it. if !s.CredsLastRotatedTime.IsZero() && ct == StaticAWS {
if err := s.DeleteCreds(); err != nil { if err := s.DeleteCreds(ctx); err != nil {
return err return err
} }
} }
@@ -142,21 +167,22 @@ func (s *AwsCredentialPersistedState) ReplaceCreds(credentialsConfig *awsutil.Cr
// DeleteCreds deletes the credentials in the state. The access key // DeleteCreds deletes the credentials in the state. The access key
// ID, secret access key, and rotation time fields are zeroed out in // ID, secret access key, and rotation time fields are zeroed out in
// the state just to ensure that they cannot be re-used after. // the state just to ensure that they cannot be re-used after.
func (s *AwsCredentialPersistedState) DeleteCreds() error { func (s *AwsCredentialPersistedState) DeleteCreds(ctx context.Context) error {
if s.CredentialsConfig == nil { if s.CredentialsConfig == nil {
return errors.New("missing credentials config") return errors.New("missing credentials config")
} }
if GetCredentialType(s.CredentialsConfig) != StaticAWS {
return errors.New("invalid credential type")
}
if err := s.CredentialsConfig.DeleteAccessKey(s.CredentialsConfig.AccessKey, s.testOpts...); err != nil { if err := s.CredentialsConfig.DeleteAccessKey(ctx, s.CredentialsConfig.AccessKey, s.testOpts...); err != nil {
// Determine if the deletion error was due to a missing // Determine if the deletion error was due to a missing
// resource. If it was, just pass it. // resource. If it was, just pass it.
var awsErr awserr.Error var awsErr *iamTypes.NoSuchEntityException
if errors.As(err, &awsErr) { if errors.As(err, &awsErr) && awsErr.ErrorCode() == "NoSuchEntity" {
if awsErr.Code() == iam.ErrCodeNoSuchEntityException { s.CredentialsConfig = nil
s.CredentialsConfig = nil s.CredsLastRotatedTime = time.Time{}
s.CredsLastRotatedTime = time.Time{} return nil
return nil
}
} }
// Otherwise treat it as an actual error. // Otherwise treat it as an actual error.
@@ -168,10 +194,15 @@ func (s *AwsCredentialPersistedState) DeleteCreds() error {
return nil return nil
} }
// GetSession returns a configured AWS session for the credentials in // GenerateCredentialChain returns a AWS configuration for the credentials in the state.
// the state. func (s *AwsCredentialPersistedState) GenerateCredentialChain(ctx context.Context) (*aws.Config, error) {
func (s *AwsCredentialPersistedState) GetSession() (*session.Session, error) { // Default `WithSharedCredentials` is `true` - This disables the ability to
return s.CredentialsConfig.GetSession(s.testOpts...) // read credentials from environment variables because we set the default
// profile options, so we need to set it to false (see
// awsutil@v2.(*CredentialsConfig).generateAwsConfigOptions) for extra
// details.
opts := append(s.testOpts, awsutil.WithSharedCredentials(false))
return s.CredentialsConfig.GenerateCredentialChain(ctx, opts...)
} }
// ToMap returns a map of the credentials stored in the persisted state. // ToMap returns a map of the credentials stored in the persisted state.
@@ -179,7 +210,15 @@ func (s *AwsCredentialPersistedState) GetSession() (*session.Session, error) {
// return a map for long-term credentials with following keys: // return a map for long-term credentials with following keys:
// access_key_id, secret_access_key & creds_last_rotated_time // access_key_id, secret_access_key & creds_last_rotated_time
func (s *AwsCredentialPersistedState) ToMap() map[string]any { func (s *AwsCredentialPersistedState) ToMap() map[string]any {
if HasDynamicCredentials(s.CredentialsConfig.AccessKey) { ct := GetCredentialType(s.CredentialsConfig)
// Dynamic AWS credentials are temporary credentials that expire within an hour.
// ToMap() returns an empty map here so that Boundary does not store temporary
// credentials into the database.
//
// Uknown credentials are not aws compatiable secrets that may have been provided
// for a non-aws s3 compatiable service. These credentials will not be returned
// because the plugin does not know how to manage non-aws credentials.
if ct == DynamicAWS || ct == Unknown {
return map[string]any{} return map[string]any{}
} }
return map[string]any{ return map[string]any{
@@ -256,18 +295,22 @@ func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *Crede
return s, nil return s, nil
} }
// HasDynamicCredentials returns true if the access key is a dynamic credential type. // GetCredentialType returns the credential type based on the given
// // AccessKey/RoleARN. See CredentialType definition for more information.
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys func GetCredentialType(cc *awsutil.CredentialsConfig) CredentialType {
// Access key IDs beginning with ASIA are temporary credentials access keys that you create using AWS STS operations. if cc == nil {
func HasDynamicCredentials(accessKey string) bool { return Unknown
return strings.HasPrefix(accessKey, "ASIA") }
}
// HasStaticCredentials returns true if the access key is a static credential type. if len(cc.RoleARN) > 0 || strings.HasPrefix(cc.AccessKey, "ASIA") {
// return DynamicAWS
// https://docs.aws.amazon.com/IAM/latest/UserGuide/security-creds.html#sec-access-keys-and-secret-access-keys }
// Access key IDs beginning with AKIA are long-term access keys for an IAM user or an AWS account root user. if strings.HasPrefix(cc.AccessKey, "AKIA") {
func HasStaticCredentials(accessKey string) bool { return StaticAWS
return strings.HasPrefix(accessKey, "AKIA") }
if len(cc.AccessKey) > 0 {
return StaticOther
}
return Unknown
} }
+103 -79
View File
@@ -4,15 +4,16 @@
package credential package credential
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"testing" "testing"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go/service/iam" iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
@@ -383,7 +384,7 @@ func TestAwsCatalogPersistedState_ValidateCreds(t *testing.T) {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
err := tc.in.ValidateCreds() err := tc.in.ValidateCreds(context.Background())
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.EqualError(err, tc.expectedErr)
return return
@@ -407,11 +408,33 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
in: &AwsCredentialPersistedState{}, in: &AwsCredentialPersistedState{},
expectedErr: "missing credentials config", expectedErr: "missing credentials config",
}, },
{
name: "cannot delete dynamic credential type",
in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "ASIAfoobar",
SecretKey: "bazqux",
},
CredsLastRotatedTime: time.Now(),
},
expectedErr: "invalid credential type",
},
{
name: "cannot delete unknown credential type",
in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "DNEfoobar",
SecretKey: "bazqux",
},
CredsLastRotatedTime: time.Now(),
},
expectedErr: "invalid credential type",
},
{ {
name: "rotation error", name: "rotation error",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
Region: "us-west-2", Region: "us-west-2",
}, },
@@ -423,13 +446,13 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
), ),
}, },
}, },
expectedErr: fmt.Sprintf("error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr), expectedErr: fmt.Sprintf("error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
}, },
{ {
name: "good", name: "good",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
Region: "us-west-2", Region: "us-west-2",
}, },
@@ -441,7 +464,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
awsutil.NewMockIAM( awsutil.NewMockIAM(
awsutil.WithGetUserOutput( awsutil.WithGetUserOutput(
&iam.GetUserOutput{ &iam.GetUserOutput{
User: &iam.User{ User: &iamTypes.User{
Arn: aws.String("arn:aws:iam::123456789012:user/JohnDoe"), Arn: aws.String("arn:aws:iam::123456789012:user/JohnDoe"),
UserId: aws.String("AIDAJQABLZS4A3QDU576Q"), UserId: aws.String("AIDAJQABLZS4A3QDU576Q"),
UserName: aws.String("JohnDoe"), UserName: aws.String("JohnDoe"),
@@ -450,7 +473,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
), ),
awsutil.WithCreateAccessKeyOutput( awsutil.WithCreateAccessKeyOutput(
&iam.CreateAccessKeyOutput{ &iam.CreateAccessKeyOutput{
AccessKey: &iam.AccessKey{ AccessKey: &iamTypes.AccessKey{
AccessKeyId: aws.String("one"), AccessKeyId: aws.String("one"),
SecretAccessKey: aws.String("two"), SecretAccessKey: aws.String("two"),
UserName: aws.String("JohnDoe"), UserName: aws.String("JohnDoe"),
@@ -474,7 +497,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
err := tc.in.RotateCreds() err := tc.in.RotateCreds(context.Background())
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.EqualError(err, tc.expectedErr)
return return
@@ -514,7 +537,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
name: "deletion error", name: "deletion error",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
CredsLastRotatedTime: time.Now(), CredsLastRotatedTime: time.Now(),
@@ -533,7 +556,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
name: "good with delete of old rotated", name: "good with delete of old rotated",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
CredsLastRotatedTime: time.Now(), CredsLastRotatedTime: time.Now(),
@@ -557,7 +580,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
name: "good without delete of old rotated", name: "good without delete of old rotated",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
testOpts: []awsutil.Option{ testOpts: []awsutil.Option{
@@ -583,7 +606,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
state.Reset() state.Reset()
err := tc.in.ReplaceCreds(tc.credentialConfig) err := tc.in.ReplaceCreds(context.Background(), tc.credentialConfig)
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.EqualError(err, tc.expectedErr)
return return
@@ -609,11 +632,33 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
in: &AwsCredentialPersistedState{}, in: &AwsCredentialPersistedState{},
expectedErr: "missing credentials config", expectedErr: "missing credentials config",
}, },
{
name: "cannot delete dynamic credential type",
in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "ASIAfoobar",
SecretKey: "bazqux",
},
CredsLastRotatedTime: time.Now(),
},
expectedErr: "invalid credential type",
},
{
name: "cannot delete unknown credential type",
in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "DNEfoobar",
SecretKey: "bazqux",
},
CredsLastRotatedTime: time.Now(),
},
expectedErr: "invalid credential type",
},
{ {
name: "deletion error", name: "deletion error",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
testOpts: []awsutil.Option{ testOpts: []awsutil.Option{
@@ -630,14 +675,14 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
name: "deletion error, but OK because key was just gone", name: "deletion error, but OK because key was just gone",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
testOpts: []awsutil.Option{ testOpts: []awsutil.Option{
awsutil.WithIAMAPIFunc( awsutil.WithIAMAPIFunc(
awsutil.NewMockIAM( awsutil.NewMockIAM(
awsutil.WithDeleteAccessKeyError( awsutil.WithDeleteAccessKeyError(
awserr.New(iam.ErrCodeNoSuchEntityException, "", nil), &iamTypes.NoSuchEntityException{},
), ),
), ),
), ),
@@ -648,7 +693,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
name: "good", name: "good",
in: &AwsCredentialPersistedState{ in: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{ CredentialsConfig: &awsutil.CredentialsConfig{
AccessKey: "foobar", AccessKey: "AKIAfoobar",
SecretKey: "bazqux", SecretKey: "bazqux",
}, },
testOpts: []awsutil.Option{ testOpts: []awsutil.Option{
@@ -664,7 +709,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
err := tc.in.DeleteCreds() err := tc.in.DeleteCreds(context.Background())
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.EqualError(err, tc.expectedErr)
return return
@@ -677,7 +722,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
} }
} }
func TestAwsCatalogPersistedState_GetSession(t *testing.T) { func TestAwsCatalogPersistedState_GenerateCredentialChain(t *testing.T) {
require := require.New(t) require := require.New(t)
cases := []struct { cases := []struct {
name string name string
@@ -693,7 +738,7 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
}, },
testOpts: []awsutil.Option{awsutil.MockOptionErr(errors.New(testOptionErr))}, testOpts: []awsutil.Option{awsutil.MockOptionErr(errors.New(testOptionErr))},
}, },
expectedErr: fmt.Sprintf("error reading options in GetSession: %s", testOptionErr), expectedErr: fmt.Sprintf("error reading options in GenerateCredentialChain: %s", testOptionErr),
}, },
{ {
name: "static credentials", name: "static credentials",
@@ -736,7 +781,7 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
_, err := tc.in.GetSession() _, err := tc.in.GenerateCredentialChain(context.Background())
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.EqualError(err, tc.expectedErr)
return return
@@ -746,74 +791,53 @@ func TestAwsCatalogPersistedState_GetSession(t *testing.T) {
} }
} }
func TestHasStaticCredentials(t *testing.T) { func TestGetCredentialType(t *testing.T) {
require := require.New(t) tests := []struct {
cases := []struct { name string
name string credConfig *awsutil.CredentialsConfig
accessKey string expectedCredType CredentialType
expected bool
}{ }{
{ {
name: "dynamic credential", name: "nil credential config",
accessKey: "ASIAEXAMPLE", credConfig: nil,
expected: false, expectedCredType: Unknown,
}, },
{ {
name: "static credential", name: "empty credential config",
accessKey: "AKIAEXAMPLE", credConfig: &awsutil.CredentialsConfig{},
expected: true, expectedCredType: Unknown,
}, },
{ {
name: "other credential", name: "static aws",
accessKey: "EXAMPLE", credConfig: &awsutil.CredentialsConfig{AccessKey: "AKIAfoobar"},
expected: false, expectedCredType: StaticAWS,
}, },
{ {
name: "empty", name: "static other",
accessKey: "", credConfig: &awsutil.CredentialsConfig{AccessKey: "cK3kNFa24foobar"},
expected: false, expectedCredType: StaticOther,
},
{
name: "dynamic aws - role arn",
credConfig: &awsutil.CredentialsConfig{RoleARN: "arn:aws:iam::123456789012:role/S3Access"},
expectedCredType: DynamicAWS,
},
{
name: "dynamic aws - access key",
credConfig: &awsutil.CredentialsConfig{AccessKey: "ASIAfoobar"},
expectedCredType: DynamicAWS,
},
{
name: "dynamic aws - role arn and access key",
credConfig: &awsutil.CredentialsConfig{AccessKey: "ASIAfoobar", RoleARN: "arn:aws:iam::123456789012:role/S3Access"},
expectedCredType: DynamicAWS,
}, },
} }
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require.Equal(tc.expected, HasStaticCredentials(tc.accessKey))
})
}
}
func TestHasDynamicCredentials(t *testing.T) { for _, tc := range tests {
require := require.New(t)
cases := []struct {
name string
accessKey string
expected bool
}{
{
name: "dynamic credential",
accessKey: "ASIAEXAMPLE",
expected: true,
},
{
name: "static credential",
accessKey: "AKIAEXAMPLE",
expected: false,
},
{
name: "other credential",
accessKey: "EXAMPLE",
expected: false,
},
{
name: "empty",
accessKey: "",
expected: false,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require.Equal(tc.expected, HasDynamicCredentials(tc.accessKey)) ct := GetCredentialType(tc.credConfig)
require.Equal(t, tc.expectedCredType, ct)
}) })
} }
} }
+10 -9
View File
@@ -4,10 +4,11 @@
package credential package credential
import ( import (
"github.com/aws/aws-sdk-go/aws/session" "context"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/iam/iamiface" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
@@ -27,25 +28,25 @@ func (s *testMockIAMState) Reset() {
} }
type testMockIAM struct { type testMockIAM struct {
iamiface.IAMAPI awsutil.IAMClient
State *testMockIAMState State *testMockIAMState
} }
func (m *testMockIAM) DeleteAccessKey(input *iam.DeleteAccessKeyInput) (*iam.DeleteAccessKeyOutput, error) { func (m *testMockIAM) DeleteAccessKey(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) {
m.State.DeleteAccessKeyCalled = true m.State.DeleteAccessKeyCalled = true
return m.IAMAPI.DeleteAccessKey(input) return m.IAMClient.DeleteAccessKey(ctx, input, opts...)
} }
func newTestMockIAM(state *testMockIAMState, opts ...awsutil.MockIAMOption) awsutil.IAMAPIFunc { func newTestMockIAM(state *testMockIAMState, opts ...awsutil.MockIAMOption) awsutil.IAMAPIFunc {
return func(sess *session.Session) (iamiface.IAMAPI, error) { return func(awsConfig *aws.Config) (awsutil.IAMClient, error) {
m := &testMockIAM{ m := &testMockIAM{
State: state, State: state,
} }
f := awsutil.NewMockIAM(opts...) f := awsutil.NewMockIAM(opts...)
var err error var err error
m.IAMAPI, err = f(sess) m.IAMClient, err = f(awsConfig)
return m, err return m, err
} }
} }
+11 -24
View File
@@ -83,15 +83,14 @@ func (p *HostPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalo
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
} }
// Try to rotate static credentials if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
if !catalogAttributes.DisableCredentialRotation { if !catalogAttributes.DisableCredentialRotation {
if err := credState.RotateCreds(); err != nil { if err := credState.RotateCreds(ctx); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
} }
} else { } else {
// Simply validate if we aren't rotating. // Simply validate if we aren't rotating.
if err := credState.ValidateCreds(); err != nil { if err := credState.ValidateCreds(ctx); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
} }
} }
@@ -160,7 +159,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) { if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
if catalogAttributes.DisableCredentialRotation && !updateSecrets { if catalogAttributes.DisableCredentialRotation && !updateSecrets {
// This is a validate check to make sure that we aren't disabling // This is a validate check to make sure that we aren't disabling
// rotation for credentials currently being managed by rotation. // rotation for credentials currently being managed by rotation.
@@ -179,7 +178,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
// Replace the credentials. This checks the timestamp on the last // Replace the credentials. This checks the timestamp on the last
// rotation time as well and deletes the credentials if we are // rotation time as well and deletes the credentials if we are
// managing them (ie: if we've rotated them before). // managing them (ie: if we've rotated them before).
if err := credState.ReplaceCreds(catalogSecrets); err != nil { if err := credState.ReplaceCreds(ctx, catalogSecrets); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
} }
} }
@@ -187,7 +186,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
if !catalogAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() { if !catalogAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
// If we're enabling rotation now but didn't before, or have // If we're enabling rotation now but didn't before, or have
// freshly replaced credentials, we can rotate here. // freshly replaced credentials, we can rotate here.
if err := credState.RotateCreds(); err != nil { if err := credState.RotateCreds(ctx); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
} }
} }
@@ -244,12 +243,12 @@ func (p *HostPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalo
} }
// try to delete static credentials // try to delete static credentials
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) { if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
if !credState.CredsLastRotatedTime.IsZero() { if !credState.CredsLastRotatedTime.IsZero() {
// Delete old/existing credentials. This is done with the same // Delete old/existing credentials. This is done with the same
// credentials to ensure that it has the proper permissions to do // credentials to ensure that it has the proper permissions to do
// it. // it.
if err := credState.DeleteCreds(); err != nil { if err := credState.DeleteCreds(ctx); err != nil {
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during catalog deletion: %s", err) return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during catalog deletion: %s", err)
} }
} }
@@ -332,11 +331,7 @@ func (p *HostPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest
return nil, err return nil, err
} }
opts := []ec2Option{} ec2Client, err := catalogState.EC2Client(ctx)
if catalogAttributes.Region != "" {
opts = append(opts, WithRegion(catalogAttributes.Region))
}
ec2Client, err := catalogState.EC2Client(ctx, opts...)
if err != nil { if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
@@ -409,11 +404,7 @@ func (p *HostPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest
return nil, err return nil, err
} }
opts := []ec2Option{} ec2Client, err := catalogState.EC2Client(ctx)
if catalogAttributes.Region != "" {
opts = append(opts, WithRegion(catalogAttributes.Region))
}
ec2Client, err := catalogState.EC2Client(ctx, opts...)
if err != nil { if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
@@ -516,11 +507,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
} }
} }
opts := []ec2Option{} ec2Client, err := catalogState.EC2Client(ctx)
if catalogAttributes.Region != "" {
opts = append(opts, WithRegion(catalogAttributes.Region))
}
ec2Client, err := catalogState.EC2Client(ctx, opts...)
if err != nil { if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
+3 -3
View File
@@ -17,7 +17,7 @@ import (
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin" pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -153,7 +153,7 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr), expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
expectedErrCode: codes.InvalidArgument, expectedErrCode: codes.InvalidArgument,
}, },
{ {
@@ -411,7 +411,7 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr), expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling iam.GetUser: %s", testGetUserErr),
expectedErrCode: codes.InvalidArgument, expectedErrCode: codes.InvalidArgument,
}, },
} }
+7 -68
View File
@@ -12,8 +12,6 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin" pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
@@ -88,75 +86,16 @@ func (s *awsCatalogPersistedState) toProto() (*pb.HostCatalogPersisted, error) {
// EC2Client returns a configured EC2 client based on the session // EC2Client returns a configured EC2 client based on the session
// information stored in the state. // information stored in the state.
func (s *awsCatalogPersistedState) EC2Client(ctx context.Context, opt ...ec2Option) (EC2API, error) { func (s *awsCatalogPersistedState) EC2Client(ctx context.Context) (EC2API, error) {
opts, err := getOpts(opt...) awsCfg, err := s.GenerateCredentialChain(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing options when fetching EC2 client: %w", err) return nil, fmt.Errorf("error getting AWS configuration when fetching EC2 client: %w", err)
} }
if awsCfg == nil {
awsConfigOpts := []func(*config.LoadOptions) error{} return nil, fmt.Errorf("nil aws configuration")
sess, err := s.GetSession()
if err != nil {
return nil, fmt.Errorf("error getting AWS session when fetching EC2 client: %w", err)
} }
creds, err := sess.Config.Credentials.Get()
if err != nil {
return nil, fmt.Errorf("error getting AWS session when fetching EC2 client: %w", err)
}
customCredentials := config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken),
)
awsConfigOpts = append(awsConfigOpts, customCredentials)
if sess.Config.Region != nil {
awsConfigOpts = append(awsConfigOpts, config.WithRegion(*sess.Config.Region))
}
if opts.withRegion != "" {
awsConfigOpts = append(awsConfigOpts, config.WithRegion(opts.withRegion))
}
cfg, err := config.LoadDefaultConfig(ctx, awsConfigOpts...)
if err != nil {
return nil, fmt.Errorf("error loading aws configuration when fetching EC2 client: %w", err)
}
if s.testEC2APIFunc != nil { if s.testEC2APIFunc != nil {
return s.testEC2APIFunc(cfg) return s.testEC2APIFunc(*awsCfg)
}
cfg.HTTPClient = customClient
return ec2.NewFromConfig(cfg), nil
}
// getOpts iterates the inbound ec2Options and returns a struct
func getOpts(opt ...ec2Option) (ec2Options, error) {
opts := getDefaultEC2Options()
for _, o := range opt {
if o == nil {
continue
}
if err := o(&opts); err != nil {
return ec2Options{}, err
}
}
return opts, nil
}
// ec2Option - how ec2Options are passed as arguments
type ec2Option func(*ec2Options) error
// options = how options are represented
type ec2Options struct {
withRegion string
}
func getDefaultEC2Options() ec2Options {
return ec2Options{}
}
// WithRegion contains the region to use
func WithRegion(with string) ec2Option {
return func(o *ec2Options) error {
o.withRegion = with
return nil
} }
return ec2.NewFromConfig(*awsCfg), nil
} }
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"testing" "testing"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -29,7 +29,7 @@ func TestAwsCatalogPersistedStateEC2Client(t *testing.T) {
) )
require.NoError(err) require.NoError(err)
clientRaw, err := state.EC2Client(context.Background(), WithRegion("us-west-2")) clientRaw, err := state.EC2Client(context.Background())
require.NoError(err) require.NoError(err)
require.NotNil(clientRaw) require.NotNil(clientRaw)
client, ok := clientRaw.(*testMockEC2) client, ok := clientRaw.(*testMockEC2)
+214 -77
View File
@@ -12,6 +12,7 @@ import (
"io" "io"
"os" "os"
"path" "path"
"strings"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/retry" "github.com/aws/aws-sdk-go-v2/aws/retry"
@@ -76,17 +77,10 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
} }
// Try to rotate static credentials // Try to rotate AWS static credentials
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) { if cred.GetCredentialType(credConfig) == cred.StaticAWS && !storageAttributes.DisableCredentialRotation {
if !storageAttributes.DisableCredentialRotation { if err := credState.RotateCreds(ctx); err != nil {
if err := credState.RotateCreds(); err != nil { return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
}
} else {
// Simply validate if we aren't rotating.
if err := credState.ValidateCreds(); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
}
} }
} }
@@ -119,76 +113,100 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
// For storage buckets we only need to really validate what is // For storage buckets we only need to really validate what is
// currently in the new copy of the storage bucket data, so skip // currently in the new copy of the storage bucket data, so skip
// fetching the new copy to keep things a little less messy. // fetching the new copy to keep things a little less messy.
bucket := req.GetNewBucket() newBucket := req.GetNewBucket()
if bucket == nil { if newBucket == nil {
return nil, status.Error(codes.InvalidArgument, "new bucket is required") return nil, status.Error(codes.InvalidArgument, "new bucket is required")
} }
if bucket.BucketName == "" { if newBucket.BucketName == "" {
return nil, status.Error(codes.InvalidArgument, "new bucketName is required") return nil, status.Error(codes.InvalidArgument, "new bucketName is required")
} }
currentBucket := req.GetCurrentBucket()
var updateSecrets bool if currentBucket == nil {
secrets := bucket.GetSecrets() return nil, status.Error(codes.InvalidArgument, "current bucket is required")
if secrets != nil {
// We will be updating secrets this run, but what exactly that
// means will be determined later.
updateSecrets = true
} }
attrs := bucket.GetAttributes() oldAttrs := currentBucket.GetAttributes()
if attrs == nil { if oldAttrs == nil {
return nil, status.Error(codes.InvalidArgument, "current bucket attributes is required")
}
oldStorageAttributes, err := getStorageAttributes(oldAttrs)
if err != nil {
return nil, err
}
newAttrs := newBucket.GetAttributes()
if newAttrs == nil {
return nil, status.Error(codes.InvalidArgument, "new bucket attributes is required") return nil, status.Error(codes.InvalidArgument, "new bucket attributes is required")
} }
newStorageAttributes, err := getStorageAttributes(newAttrs)
storageAttributes, err := getStorageAttributes(attrs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Get the persisted data.
// NOTE: We might need to change this at a later time. I'm not too
// sure *exactly* what scenarios we might encounter that would
// ultimately mean that we would have to handle an empty or missing
// state in update, but what we are ultimately assuming here
// (implicitly through awsStoragePersistedStateFromProto) is that
// the state will exist and be populated. Personally I think this
// is fine and important, but this may change in the future.
credState, err := cred.AwsCredentialPersistedStateFromProto( credState, err := cred.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetData(), req.GetPersisted().GetData(),
storageAttributes.CredentialAttributes, oldStorageAttributes.CredentialAttributes,
p.testCredStateOpts...) p.testCredStateOpts...)
if err != nil { if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) { // Verify the incoming credentials are valid and return any errors to the
if storageAttributes.DisableCredentialRotation && !updateSecrets { // user if they're not. Note this doesn't validate the credentials against
// This is a validate check to make sure that we aren't disabling // AWS - it only does logical validation on the fields.
// rotation for credentials currently being managed by rotation. updatedCredentials, err := cred.GetCredentialsConfig(newBucket.GetSecrets(), newStorageAttributes.CredentialAttributes, false)
// This is not allowed. if err != nil {
return nil, err
}
// We will be updating credentials when one of two changes occur:
// 1. the new bucket has secrets for static credentials, in which case we need to delete the old static credentials
// 2. the new bucket has a different roleARN value for dynamic credentials
if newBucket.GetSecrets() != nil || newStorageAttributes.RoleArn != oldStorageAttributes.RoleArn {
// Ensure the incoming credentials are valid for interaction with the S3
// Bucket before replacing them.
newCredState, err := cred.NewAwsCredentialPersistedState(
append([]cred.AwsCredentialPersistedStateOption{
cred.WithCredentialsConfig(updatedCredentials),
}, p.testCredStateOpts...)...,
)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error setting up new credential persisted state: %s", err)
}
newStorageState, err := newAwsStoragePersistedState(
append([]awsStoragePersistedStateOption{
withCredentials(newCredState),
}, p.testStorageStateOpts...)...,
)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
}
if err := dryRunValidation(ctx, newStorageState, newStorageAttributes, newBucket); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to validate incoming credentials: %s", err)
}
// Replace the existing credential state.
// This checks the timestamp on the last rotation time as well
// and deletes the credentials if we are managing them
// (ie: if we've rotated them before).
if err := credState.ReplaceCreds(ctx, updatedCredentials); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
}
}
if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
// This is a validate check to make sure that we aren't disabling
// rotation for credentials currently being managed by rotation.
// This is not allowed.
if newStorageAttributes.DisableCredentialRotation && newBucket.GetSecrets() == nil {
if !credState.CredsLastRotatedTime.IsZero() { if !credState.CredsLastRotatedTime.IsZero() {
return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials") return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
} }
} }
if updateSecrets { // If we're enabling rotation now but didn't before, or have
storageSecrets, err := cred.GetCredentialsConfig(secrets, storageAttributes.CredentialAttributes, false) // freshly replaced credentials, we can rotate here.
if err != nil { if !newStorageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
return nil, err if err := credState.RotateCreds(ctx); err != nil {
}
// Replace the credentials. This checks the timestamp on the last
// rotation time as well and deletes the credentials if we are
// managing them (ie: if we've rotated them before).
if err := credState.ReplaceCreds(storageSecrets); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
}
}
if !storageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
// If we're enabling rotation now but didn't before, or have
// freshly replaced credentials, we can rotate here.
if err := credState.RotateCreds(); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
} }
} }
@@ -204,7 +222,7 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
} }
// perform dry run to ensure we can interact with the bucket as expected. // perform dry run to ensure we can interact with the bucket as expected.
if err := dryRunValidation(ctx, storageState, storageAttributes, bucket); err != nil { if err := dryRunValidation(ctx, storageState, newStorageAttributes, newBucket); err != nil {
return nil, err return nil, err
} }
@@ -259,12 +277,12 @@ func (p *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDel
} }
// try to delete static credentials // try to delete static credentials
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) { if cred.GetCredentialType(credState.CredentialsConfig) == cred.StaticAWS {
if !credState.CredsLastRotatedTime.IsZero() { if !credState.CredsLastRotatedTime.IsZero() {
// Delete old/existing credentials. This is done with the same // Delete old/existing credentials. This is done with the same
// credentials to ensure that it has the proper permissions to do // credentials to ensure that it has the proper permissions to do
// it. // it.
if err := credState.DeleteCreds(); err != nil { if err := credState.DeleteCreds(ctx); err != nil {
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err) return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err)
} }
} }
@@ -323,9 +341,6 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
} }
opts := []s3Option{} opts := []s3Option{}
if storageAttributes.Region != "" {
opts = append(opts, WithRegion(storageAttributes.Region))
}
if storageAttributes.EndpointUrl != "" { if storageAttributes.EndpointUrl != "" {
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl)) opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
} }
@@ -452,9 +467,6 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
} }
opts := []s3Option{} opts := []s3Option{}
if storageAttributes.Region != "" {
opts = append(opts, WithRegion(storageAttributes.Region))
}
if storageAttributes.EndpointUrl != "" { if storageAttributes.EndpointUrl != "" {
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl)) opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
} }
@@ -566,9 +578,6 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
} }
opts := []s3Option{} opts := []s3Option{}
if storageAttributes.Region != "" {
opts = append(opts, WithRegion(storageAttributes.Region))
}
if storageAttributes.EndpointUrl != "" { if storageAttributes.EndpointUrl != "" {
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl)) opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
} }
@@ -614,6 +623,129 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
}, nil }, nil
} }
// DeleteObjects is used to delete one or many objects from an s3 bucket.
func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjectsRequest) (*pb.DeleteObjectsResponse, error) {
if req.GetKeyPrefix() == "" {
return nil, status.Error(codes.InvalidArgument, "key prefix is required")
}
bucket := req.GetBucket()
if bucket == nil {
return nil, status.Error(codes.InvalidArgument, "bucket is required")
}
if bucket.GetBucketName() == "" {
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
}
attrs := bucket.GetAttributes()
if attrs == nil {
return nil, status.Error(codes.InvalidArgument, "attributes is required")
}
storageAttributes, err := getStorageAttributes(attrs)
if err != nil {
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
if err != nil {
return nil, err
}
// Set up the creds in our persisted state.
credState, err := cred.NewAwsCredentialPersistedState(
append([]cred.AwsCredentialPersistedStateOption{
cred.WithCredentialsConfig(credConfig),
}, p.testCredStateOpts...)...,
)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
}
storageState, err := newAwsStoragePersistedState(
append([]awsStoragePersistedStateOption{
withCredentials(credState),
}, p.testStorageStateOpts...)...,
)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
}
opts := []s3Option{}
if storageAttributes.EndpointUrl != "" {
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
}
client, err := storageState.S3Client(ctx, opts...)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error getting S3 client: %s", err)
}
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
// path.Join ends by "cleaning" the path, including removing a trailing slash, if
// it exists. given that a slash is used to denote a folder, it is required here.
prefix += "/"
}
if !req.Recursive {
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(prefix),
})
if err != nil {
return nil, parseAWSErrCode("error deleting S3 object", err)
}
return &pb.DeleteObjectsResponse{
ObjectsDeleted: uint32(1),
}, nil
}
const maxkeys = 1000
objects := []types.ObjectIdentifier{}
var conToken *string
truncated := true
for truncated {
res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(bucket.GetBucketName()),
Prefix: aws.String(prefix),
MaxKeys: maxkeys,
ContinuationToken: conToken,
})
if err != nil {
return nil, parseAWSErrCode("error iterating S3 bucket contents", err)
}
truncated = res.IsTruncated
conToken = res.NextContinuationToken
for _, o := range res.Contents {
objects = append(objects, types.ObjectIdentifier{
Key: o.Key,
})
}
}
deleted := 0
for i := 0; i < len(objects); i += maxkeys {
toDelete := objects[i:min(len(objects), i+maxkeys)] // min is required to avoid an out of bounds panic
res, err := client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
Bucket: aws.String(bucket.GetBucketName()),
Delete: &types.Delete{
Objects: toDelete,
},
})
if err != nil {
return nil, parseAWSErrCode("error deleting S3 object(s)", err)
}
deleted += len(res.Deleted)
}
return &pb.DeleteObjectsResponse{
ObjectsDeleted: uint32(deleted),
}, nil
}
// dryRunValidation validates that the IAM role policy attached to the given secrets of the // dryRunValidation validates that the IAM role policy attached to the given secrets of the
// storage bucket has the minimum required permissions needed for this plugin to function // storage bucket has the minimum required permissions needed for this plugin to function
// as expected. This function will create an object in the s3 bucket to validate it has // as expected. This function will create an object in the s3 bucket to validate it has
@@ -632,9 +764,6 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
} }
opts := []s3Option{} opts := []s3Option{}
if attrs.Region != "" {
opts = append(opts, WithRegion(attrs.Region))
}
if attrs.EndpointUrl != "" { if attrs.EndpointUrl != "" {
opts = append(opts, WithEndpoint(attrs.EndpointUrl)) opts = append(opts, WithEndpoint(attrs.EndpointUrl))
} }
@@ -667,13 +796,21 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
return parseAWSErrCode("error failed to get head object", err) return parseAWSErrCode("error failed to get head object", err)
} }
// attempt to delete the test object created for the dry run validation, if res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
// this step is allowed to fail because DeleteObject is not a required Bucket: aws.String(bucket.GetBucketName()),
// operation for the plugin. Prefix: aws.String(objectKey),
client.DeleteObject(ctx, &s3.DeleteObjectInput{ }); err != nil {
return parseAWSErrCode("error failed to list objects", err)
} else if res == nil || len(res.Contents) != 1 || *res.Contents[0].Key != objectKey {
return status.Errorf(codes.InvalidArgument, "list response did not contain the expected key: %+v", res)
}
if _, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(bucket.GetBucketName()), Bucket: aws.String(bucket.GetBucketName()),
Key: aws.String(objectKey), Key: aws.String(objectKey),
}) }); err != nil {
return parseAWSErrCode("error failed to delete object", err)
}
return nil return nil
} }
File diff suppressed because it is too large. Load diff
+42 -53
View File
@@ -9,12 +9,12 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"net/url"
"time" "time"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
smithyEndpoints "github.com/aws/smithy-go/endpoints"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
@@ -25,6 +25,8 @@ type S3API interface {
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error)
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
} }
var customClient = &http.Client{ var customClient = &http.Client{
@@ -97,55 +99,23 @@ func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err) return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
} }
awsCfg, err := s.GenerateCredentialChain(ctx)
awsConfigOpts := []func(*config.LoadOptions) error{}
sess, err := s.GetSession()
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting AWS session when fetching S3 client: %w", err) return nil, fmt.Errorf("error getting AWS configuration when fetching S3 client: %w", err)
} }
creds, err := sess.Config.Credentials.Get() if awsCfg == nil {
if err != nil { return nil, fmt.Errorf("nil aws configuration")
return nil, fmt.Errorf("error getting AWS session when fetching S3 client: %w", err)
}
customCredentials := config.WithCredentialsProvider(
credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken),
)
awsConfigOpts = append(awsConfigOpts, customCredentials)
if sess.Config.Region != nil {
awsConfigOpts = append(awsConfigOpts, config.WithRegion(*sess.Config.Region))
}
if opts.withRegion != "" {
awsConfigOpts = append(awsConfigOpts, config.WithRegion(opts.withRegion))
} }
var s3Opts []func(*s3.Options)
if opts.withEndpoint != "" { if opts.withEndpoint != "" {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) { s3Opts = append(s3Opts, s3.WithEndpointResolverV2(&endpointResolver{
if service == s3.ServiceID { endpoint: opts.withEndpoint,
if opts.withRegion != "" { }))
region = opts.withRegion
}
return aws.Endpoint{
PartitionID: "aws",
URL: opts.withEndpoint,
SigningRegion: region,
}, nil
}
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
})
awsConfigOpts = append(awsConfigOpts, config.WithEndpointResolverWithOptions(customResolver))
} }
cfg, err := config.LoadDefaultConfig(ctx, awsConfigOpts...)
if err != nil {
return nil, fmt.Errorf("error loading aws configuration when fetching S3 client: %w", err)
}
if s.testS3APIFunc != nil { if s.testS3APIFunc != nil {
return s.testS3APIFunc(cfg) return s.testS3APIFunc(*awsCfg)
} }
return s3.NewFromConfig(*awsCfg, s3Opts...), nil
cfg.HTTPClient = customClient
return s3.NewFromConfig(cfg), nil
} }
// getOpts iterates the inbound s3Options and returns a struct // getOpts iterates the inbound s3Options and returns a struct
@@ -167,7 +137,6 @@ type s3Option func(*s3Options) error
// options = how options are represented // options = how options are represented
type s3Options struct { type s3Options struct {
withRegion string
withEndpoint string withEndpoint string
} }
@@ -175,14 +144,6 @@ func getDefaultS3Options() s3Options {
return s3Options{} return s3Options{}
} }
// WithRegion contains the region to use
func WithRegion(with string) s3Option {
return func(o *s3Options) error {
o.withRegion = with
return nil
}
}
// WithEndpoint contains the endpoint to use // WithEndpoint contains the endpoint to use
func WithEndpoint(with string) s3Option { func WithEndpoint(with string) s3Option {
return func(o *s3Options) error { return func(o *s3Options) error {
@@ -190,3 +151,31 @@ func WithEndpoint(with string) s3Option {
return nil return nil
} }
} }
type endpointResolver struct {
endpoint string
}
func (e *endpointResolver) ResolveEndpoint(ctx context.Context, params s3.EndpointParameters) (resolver smithyEndpoints.Endpoint, err error) {
var uri *url.URL
resolver, err = s3.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
if err != nil {
return
}
if e.endpoint == "" {
// TODO: not sure if we should set an error or ignore the missing endpoint value and continue with the default resolver
return
}
uri, err = url.Parse(e.endpoint)
if err != nil {
return
}
if uri == nil {
// TODO: not sure if we should set an error or ignore the missing uri value and continue with the default resolver
return
}
resolver = smithyEndpoints.Endpoint{
URI: *uri,
}
return
}
+1 -11
View File
@@ -7,9 +7,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -43,7 +42,6 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
withTestS3APIFunc(newTestMockS3(nil)), withTestS3APIFunc(newTestMockS3(nil)),
}, },
s3Opts: []s3Option{ s3Opts: []s3Option{
WithRegion("us-west-2"),
WithEndpoint("0.0.0.0"), WithEndpoint("0.0.0.0"),
}, },
expectedRegion: "us-west-2", expectedRegion: "us-west-2",
@@ -73,14 +71,6 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
require.True(ok) require.True(ok)
require.NotNil(client) require.NotNil(client)
require.Equal(tc.expectedRegion, client.Region) require.Equal(tc.expectedRegion, client.Region)
if tc.expectedEndpoint != "" {
endpoint, err := client.Endpoint.ResolveEndpoint(s3.ServiceID, client.Region)
require.NoError(err)
require.Equal(tc.expectedEndpoint, endpoint.URL)
require.Equal(tc.expectedRegion, endpoint.SigningRegion)
require.Equal("aws", endpoint.PartitionID)
}
}) })
} }
} }
+122 -9
View File
@@ -13,11 +13,11 @@ import (
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go/service/sts" "github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin" pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
@@ -31,6 +31,9 @@ const (
testGetObjectErr = "test error for GetObject" testGetObjectErr = "test error for GetObject"
testPutObjectErr = "test error for PutObject" testPutObjectErr = "test error for PutObject"
testHeadObjectErr = "test error for HeadObject" testHeadObjectErr = "test error for HeadObject"
testListObjectV2Err = "test error for ListObjectV2"
testDeleteObjectErr = "test error for DeleteObject"
testDeleteObjectsErr = "test error for DeleteObjects"
) )
// throttleErr is a mocked error used for testing the aws s3 client // throttleErr is a mocked error used for testing the aws s3 client
@@ -54,6 +57,14 @@ type testMockS3State struct {
HeadObjectCalled bool HeadObjectCalled bool
HeadObjectInputParams *s3.HeadObjectInput HeadObjectInputParams *s3.HeadObjectInput
ListObjectsV2Called bool
ListObjectsV2InputParams *s3.ListObjectsV2Input
DeleteObjectCalled bool
DeleteObjectInputParams *s3.DeleteObjectInput
DeleteObjectsCalled bool
DeleteObjectsInputParams *s3.DeleteObjectsInput
} }
func (s *testMockS3State) Reset() { func (s *testMockS3State) Reset() {
@@ -64,14 +75,19 @@ func (s *testMockS3State) Reset() {
s.PutObjectBody = nil s.PutObjectBody = nil
s.HeadObjectCalled = false s.HeadObjectCalled = false
s.HeadObjectInputParams = nil s.HeadObjectInputParams = nil
s.ListObjectsV2Called = false
s.ListObjectsV2InputParams = nil
s.DeleteObjectCalled = false
s.DeleteObjectInputParams = nil
s.DeleteObjectsCalled = false
s.DeleteObjectsInputParams = nil
} }
type testMockS3 struct { type testMockS3 struct {
S3API S3API
State *testMockS3State State *testMockS3State
Region string Region string
Endpoint aws.EndpointResolverWithOptions
// mocked responses for getObject // mocked responses for getObject
GetObjectOutput *s3.GetObjectOutput GetObjectOutput *s3.GetObjectOutput
@@ -84,6 +100,18 @@ type testMockS3 struct {
// mocked responses for headObject // mocked responses for headObject
HeadObjectOutput *s3.HeadObjectOutput HeadObjectOutput *s3.HeadObjectOutput
HeadObjectErr error HeadObjectErr error
// mocked responses for ListObjectsV2 (needed for delete)
ListObjectsV2Output *s3.ListObjectsV2Output
ListObjectsV2Err error
// sometimes we need to create a wildcard output
ListObjectsV2OutputFunc func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output
// mocked responses for DeleteObject(s)
DeleteObjectOutput *s3.DeleteObjectOutput
DeleteObjectErr error
DeleteObjectsOutput *s3.DeleteObjectsOutput
DeleteObjectsErr error
} }
type testMockS3Option func(m *testMockS3) error type testMockS3Option func(m *testMockS3) error
@@ -130,6 +158,55 @@ func testMockS3WithHeadObjectError(e error) testMockS3Option {
} }
} }
func testMockS3WithListObjectsV2Output(o *s3.ListObjectsV2Output) testMockS3Option {
return func(m *testMockS3) error {
m.ListObjectsV2Output = o
return nil
}
}
func testMockS3WithListObjectsV2Error(e error) testMockS3Option {
return func(m *testMockS3) error {
m.ListObjectsV2Err = e
return nil
}
}
func testMockS3WithListObjectsV2OutputFunc(o func(*s3.ListObjectsV2Input) *s3.ListObjectsV2Output) testMockS3Option {
return func(m *testMockS3) error {
m.ListObjectsV2OutputFunc = o
return nil
}
}
func testMockS3WithDeleteObjectOutput(o *s3.DeleteObjectOutput) testMockS3Option {
return func(m *testMockS3) error {
m.DeleteObjectOutput = o
return nil
}
}
func testMockS3WithDeleteObjectError(e error) testMockS3Option {
return func(m *testMockS3) error {
m.DeleteObjectErr = e
return nil
}
}
func testMockS3WithDeleteObjectsOutput(o *s3.DeleteObjectsOutput) testMockS3Option {
return func(m *testMockS3) error {
m.DeleteObjectsOutput = o
return nil
}
}
func testMockS3WithDeleteObjectsError(e error) testMockS3Option {
return func(m *testMockS3) error {
m.DeleteObjectsErr = e
return nil
}
}
func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc { func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc {
return func(cfgs ...aws.Config) (S3API, error) { return func(cfgs ...aws.Config) (S3API, error) {
m := &testMockS3{ m := &testMockS3{
@@ -147,9 +224,6 @@ func newTestMockS3(state *testMockS3State, opts ...testMockS3Option) s3APIFunc {
if cfg.Region != "" { if cfg.Region != "" {
m.Region = cfg.Region m.Region = cfg.Region
} }
if cfg.EndpointResolverWithOptions != nil {
m.Endpoint = cfg.EndpointResolverWithOptions
}
} }
return m, nil return m, nil
@@ -197,8 +271,47 @@ func (m *testMockS3) PutObject(ctx context.Context, params *s3.PutObjectInput, o
return m.PutObjectOutput, nil return m.PutObjectOutput, nil
} }
func (m *testMockS3) ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error) {
if m.State != nil {
m.State.ListObjectsV2Called = true
m.State.ListObjectsV2InputParams = params
}
if m.ListObjectsV2Err != nil {
return nil, m.ListObjectsV2Err
}
if m.ListObjectsV2OutputFunc != nil {
return m.ListObjectsV2OutputFunc(params), nil
}
return m.ListObjectsV2Output, nil
}
func (m *testMockS3) DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) { func (m *testMockS3) DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) {
return &s3.DeleteObjectOutput{}, nil if m.State != nil {
m.State.DeleteObjectCalled = true
m.State.DeleteObjectInputParams = params
}
if m.DeleteObjectErr != nil {
return nil, m.DeleteObjectErr
}
return m.DeleteObjectOutput, nil
}
func (m *testMockS3) DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error) {
if m.State != nil {
m.State.DeleteObjectsCalled = true
m.State.DeleteObjectsInputParams = params
}
if m.DeleteObjectsErr != nil {
return nil, m.DeleteObjectsErr
}
return m.DeleteObjectsOutput, nil
} }
func (m *testMockS3) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) { func (m *testMockS3) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) {
+1 -1
View File
@@ -75,7 +75,7 @@ func TestStoragePlugin(t *testing.T) {
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj") iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
require.NoError(err) require.NoError(err)
// Start the workflow now. Set up the host catalog. Note that this // Start the workflow now. Set up the storage bucket. Note that this
// will cause the state to go out of drift above in the sense that // will cause the state to go out of drift above in the sense that
// the access key ID/secret access key will no longer be valid. We // the access key ID/secret access key will no longer be valid. We
// will assert this through the returned state. // will assert this through the returned state.
+7 -7
View File
@@ -13,10 +13,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/smithy-go"
"github.com/hashicorp/boundary-plugin-aws/internal/credential" "github.com/hashicorp/boundary-plugin-aws/internal/credential"
"github.com/hashicorp/boundary-plugin-aws/internal/values" "github.com/hashicorp/boundary-plugin-aws/internal/values"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil/v2"
tfjson "github.com/hashicorp/terraform-json" tfjson "github.com/hashicorp/terraform-json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
@@ -293,7 +293,7 @@ func requireCredentialsInvalid(t *testing.T, accessKeyId, secretAccessKey string
defer cancel() defer cancel()
waitErr: waitErr:
for { for {
_, err = c.GetCallerIdentity() _, err = c.GetCallerIdentity(context.Background())
if err != nil { if err != nil {
break break
} }
@@ -308,9 +308,9 @@ waitErr:
} }
require.NotNil(err) require.NotNil(err)
awsErr, ok := err.(awserr.Error) var ae smithy.APIError
require.True(ok) require.True(errors.As(err, &ae))
require.Equal("InvalidClientTokenId", awsErr.Code()) require.Equal("InvalidClientTokenId", ae.ErrorCode())
} }
func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string) { func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string) {
@@ -322,7 +322,7 @@ func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string)
awsutil.WithSecretKey(secretAccessKey), awsutil.WithSecretKey(secretAccessKey),
) )
require.NoError(err) require.NoError(err)
_, err = c.GetCallerIdentity() _, err = c.GetCallerIdentity(context.Background())
require.NoError(err) require.NoError(err)
} }