mirror of
https://github.com/opentofu/terraform-provider-vault.git
synced 2026-01-11 19:46:35 +00:00
auth/aws: Enhance AWS authentication with role assumption and custom endpoints. (#2679)
* auth/aws: Enhance AWS authentication with role assumption and custom endpoints. - Refactor `getLoginData` to support role assumption using STS. - Introduce custom endpoint resolvers for STS and IAM services. - Update `getCredentialsConfig` to utilize new options for role ARN and session name. - Implement `generateLoginData` to create presigned requests for AWS API calls. - Add unit tests for role assumption logic, session token handling, and custom endpoint configuration. - Migrate to `awsutil/v2` for improved credential management. * auth/aws: Update CHANGELOG. * Refactor AWS credential retrieval in generateLoginData for clear error handling.
This commit is contained in:
parent
40b17648bc
commit
5f7ef99373
5 changed files with 595 additions and 57 deletions
|
|
@ -10,7 +10,12 @@ IMPROVEMENTS:
|
|||
* Add support for networking allowlist fields (`allowed_ipv4_addresses`, `allowed_ipv6_addresses`, `allowed_ports`, `disable_strict_networking`) in `vault_secrets_sync_vercel_destination` resource. Requires Vault 1.19+. ([#2681](https://github.com/hashicorp/terraform-provider-vault/pull/2681))
|
||||
* Add support for `tls_server_name` , `local_datacenter`, `socket_keep_alive`, `consistency` and `username_template` parameters for Cassandra in `vault_database_secret_backend_connection` resource. ([#2677](https://github.com/hashicorp/terraform-provider-vault/pull/2677))
|
||||
* `vault_secrets_sync_aws_destination`: Add support for networking configuration parameters `allowed_ipv4_addresses`, `allowed_ipv6_addresses`, `allowed_ports`, and `disable_strict_networking` to control outbound connections from Vault to AWS Secrets Manager. Requires Vault 1.19.0+.([#2698](https://github.com/hashicorp/terraform-provider-vault/pull/2698))
|
||||
* Updated dependencies:
|
||||
* `github.com/hashicorp/go-secure-stdlib/awsutil` v0.3.0 -> v2.1.1
|
||||
|
||||
BUGS:
|
||||
|
||||
* `provider/auth_login_aws`: Fix issue where AWS authentication with IAM role assumption (`aws_role_arn`) was not working correctly due to incorrect credential handling ([#2679](https://github.com/hashicorp/terraform-provider-vault/pull/2679))
|
||||
|
||||
## 5.6.0 (December 19, 2025)
|
||||
|
||||
|
|
@ -28,7 +33,6 @@ BUGS:
|
|||
|
||||
* Fix LDAP auth tune block read failure caused by extra /tune segment in the API request path ([#2676](https://github.com/hashicorp/terraform-provider-vault/pull/2676))
|
||||
|
||||
|
||||
## 5.5.0 (Nov 19, 2025)
|
||||
|
||||
BEHAVIOR CHANGES: With v5.5.0, the default value for `deny_null_bind` in the `vault_ldap_auth_backend` resource has changed from `false` to `true`
|
||||
|
|
|
|||
16
go.mod
16
go.mod
|
|
@ -9,6 +9,10 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.5
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.38.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1
|
||||
github.com/aws/smithy-go v1.22.1
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/coreos/pkg v0.0.0-20230601102743-20bbbf26f4d8
|
||||
github.com/denisenkom/go-mssqldb v0.12.3
|
||||
|
|
@ -24,7 +28,7 @@ require (
|
|||
github.com/hashicorp/go-hclog v1.6.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0
|
||||
github.com/hashicorp/go-version v1.8.0
|
||||
github.com/hashicorp/terraform-plugin-framework v1.16.1
|
||||
|
|
@ -66,6 +70,16 @@ require (
|
|||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
|
|
|
|||
42
go.sum
42
go.sum
|
|
@ -71,9 +71,36 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.38.1 h1:hfkzDZHBp9jAT4zcd5mtqckpU4E3Ax0LQaEWWk1VgN8=
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.38.1/go.mod h1:u36ahDtZcQHGmVm/r+0L1sfKX4fzLEMdCqiKRKkUMVM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg=
|
||||
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
|
||||
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
|
|
@ -109,7 +136,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
|
|||
github.com/coreos/pkg v0.0.0-20230601102743-20bbbf26f4d8 h1:NrLmX9HDyGvQhyZdrDx89zCvPdxQ/EHCo+xGNrjNmHc=
|
||||
github.com/coreos/pkg v0.0.0-20230601102743-20bbbf26f4d8/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -173,7 +199,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
|||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
|
|
@ -275,7 +300,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0=
|
||||
github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6 h1:kBoJV4Xl5FLtBfnBjDvBxeNSy2IRITSGs73HQsFUEjY=
|
||||
|
|
@ -303,8 +327,8 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU
|
|||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 h1:I8bynUKMh9I7JdwtW9voJ0xmHvBpxQtLjrMFDYmhOxY=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1 h1:rXE5JmHT14VYLVm+hHSqBOojPl0rlBqJx6YLikUuCgM=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.1.1/go.mod h1:6+rVulOPNCQbL3Xv2iLCqM0JmU2WO2wRzP1C6hBKeB8=
|
||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||
github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.1 h1:VaLXp47MqD1Y2K6QVrA9RooQiPyCgAbnfeJg44wKuJk=
|
||||
|
|
@ -456,8 +480,6 @@ github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5Xum
|
|||
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||
github.com/jimlambrt/gldap v0.1.13 h1:jxmVQn0lfmFbM9jglueoau5LLF/IGRti0SKf0vB753M=
|
||||
github.com/jimlambrt/gldap v0.1.13/go.mod h1:nlC30c7xVphjImg6etk7vg7ZewHCCvl1dfAhO3ZJzPg=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
|
|
@ -486,7 +508,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
|
@ -626,7 +647,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
|
|
@ -674,7 +694,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
|
|
@ -784,7 +803,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
|
|
|||
|
|
@ -4,11 +4,22 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
smithyendpoints "github.com/aws/smithy-go/endpoints"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/vault/api"
|
||||
|
||||
|
|
@ -184,7 +195,8 @@ func (l *AuthLoginAWS) Login(client *api.Client) (*api.Secret, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
loginData, err := l.getLoginData(getHCLogger())
|
||||
ctx := context.Background()
|
||||
loginData, err := l.getLoginData(ctx, getHCLogger())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get AWS credentials required for Vault login, err=%w", err)
|
||||
}
|
||||
|
|
@ -247,71 +259,225 @@ func (l *AuthLoginAWS) getDefaults() authDefaults {
|
|||
return defaults
|
||||
}
|
||||
|
||||
func (l *AuthLoginAWS) getLoginData(logger hclog.Logger) (map[string]interface{}, error) {
|
||||
func (l *AuthLoginAWS) getLoginData(ctx context.Context, logger hclog.Logger) (map[string]interface{}, error) {
|
||||
// Get credentials configuration
|
||||
config, err := l.getCredentialsConfig(logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
creds, err := config.GenerateCredentialChain()
|
||||
awsConfig, err := config.GenerateCredentialChain(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if we need to assume a role
|
||||
var roleARN string
|
||||
if v, ok := l.params[consts.FieldAWSRoleARN].(string); ok && v != "" {
|
||||
roleARN = v
|
||||
|
||||
// Create STS client with base credentials and custom endpoint if configured
|
||||
var stsOpts []func(*sts.Options)
|
||||
if v, ok := l.params[consts.FieldAWSSTSEndpoint].(string); ok && v != "" {
|
||||
stsOpts = append(stsOpts, sts.WithEndpointResolverV2(&customSTSEndpointResolver{endpointURL: v}))
|
||||
}
|
||||
stsClient := sts.NewFromConfig(*awsConfig, stsOpts...)
|
||||
|
||||
// Get role session name
|
||||
roleSessionName := "vault-provider-session"
|
||||
if v, ok := l.params[consts.FieldAWSRoleSessionName].(string); ok && v != "" {
|
||||
roleSessionName = v
|
||||
}
|
||||
|
||||
// Call AssumeRole
|
||||
assumeRoleOutput, err := stsClient.AssumeRole(ctx, &sts.AssumeRoleInput{
|
||||
RoleArn: aws.String(roleARN),
|
||||
RoleSessionName: aws.String(roleSessionName),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to assume role %s: %w", roleARN, err)
|
||||
}
|
||||
|
||||
// Create a new config with the assumed role credentials
|
||||
awsConfig = &aws.Config{
|
||||
Region: awsConfig.Region,
|
||||
Credentials: aws.NewCredentialsCache(
|
||||
aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
|
||||
return aws.Credentials{
|
||||
AccessKeyID: aws.ToString(assumeRoleOutput.Credentials.AccessKeyId),
|
||||
SecretAccessKey: aws.ToString(assumeRoleOutput.Credentials.SecretAccessKey),
|
||||
SessionToken: aws.ToString(assumeRoleOutput.Credentials.SessionToken),
|
||||
Source: "ManualAssumeRole",
|
||||
CanExpire: true,
|
||||
Expires: *assumeRoleOutput.Credentials.Expiration,
|
||||
}, nil
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
var headerValue string
|
||||
if v, ok := l.params[consts.FieldHeaderValue].(string); ok {
|
||||
headerValue = v
|
||||
}
|
||||
|
||||
return awsutil.GenerateLoginData(creds, headerValue, config.Region, logger)
|
||||
return generateLoginData(ctx, awsConfig, headerValue, logger)
|
||||
}
|
||||
|
||||
// customSTSEndpointResolver creates an endpoint resolver for STS with a custom endpoint URL
|
||||
type customSTSEndpointResolver struct {
|
||||
endpointURL string
|
||||
}
|
||||
|
||||
func (r *customSTSEndpointResolver) ResolveEndpoint(ctx context.Context, params sts.EndpointParameters) (smithyendpoints.Endpoint, error) {
|
||||
// Parse the custom endpoint URL
|
||||
uri, err := url.Parse(r.endpointURL)
|
||||
if err != nil {
|
||||
return smithyendpoints.Endpoint{}, fmt.Errorf("failed to parse custom STS endpoint URL: %w", err)
|
||||
}
|
||||
|
||||
// Return custom endpoint
|
||||
return smithyendpoints.Endpoint{
|
||||
URI: *uri,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// customIAMEndpointResolver creates an endpoint resolver for IAM with a custom endpoint URL
|
||||
type customIAMEndpointResolver struct {
|
||||
endpointURL string
|
||||
}
|
||||
|
||||
func (r *customIAMEndpointResolver) ResolveEndpoint(ctx context.Context, params iam.EndpointParameters) (smithyendpoints.Endpoint, error) {
|
||||
// Parse the custom endpoint URL
|
||||
uri, err := url.Parse(r.endpointURL)
|
||||
if err != nil {
|
||||
return smithyendpoints.Endpoint{}, fmt.Errorf("failed to parse custom IAM endpoint URL: %w", err)
|
||||
}
|
||||
|
||||
// Return custom endpoint
|
||||
return smithyendpoints.Endpoint{
|
||||
URI: *uri,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *AuthLoginAWS) getCredentialsConfig(logger hclog.Logger) (*awsutil.CredentialsConfig, error) {
|
||||
// we do not leverage awsutil.Options here since awsutil.NewCredentialsConfig
|
||||
// does not currently support all that we do.
|
||||
config, err := awsutil.NewCredentialsConfig()
|
||||
// Build options for NewCredentialsConfig
|
||||
var opts []awsutil.Option
|
||||
|
||||
if v, ok := l.params[consts.FieldAWSAccessKeyID].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithAccessKey(v))
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSSecretAccessKey].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithSecretKey(v))
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRegion].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithRegion(v))
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRoleARN].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithRoleArn(v))
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRoleSessionName].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithRoleSessionName(v))
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSWebIdentityTokenFile].(string); ok && v != "" {
|
||||
opts = append(opts, awsutil.WithWebIdentityTokenFile(v))
|
||||
}
|
||||
|
||||
opts = append(opts, awsutil.WithLogger(logger))
|
||||
|
||||
config, err := awsutil.NewCredentialsConfig(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSAccessKeyID].(string); ok && v != "" {
|
||||
config.AccessKey = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSSecretAccessKey].(string); ok && v != "" {
|
||||
config.SecretKey = v
|
||||
}
|
||||
|
||||
// Set fields that aren't available through options
|
||||
if v, ok := l.params[consts.FieldAWSProfile].(string); ok && v != "" {
|
||||
config.Profile = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSSharedCredentialsFile].(string); ok && v != "" {
|
||||
config.Filename = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSWebIdentityTokenFile].(string); ok && v != "" {
|
||||
config.WebIdentityTokenFile = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRoleARN].(string); ok && v != "" {
|
||||
config.RoleARN = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRoleSessionName].(string); ok && v != "" {
|
||||
config.RoleSessionName = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSRegion].(string); ok && v != "" {
|
||||
config.Region = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSSessionToken].(string); ok && v != "" {
|
||||
config.SessionToken = v
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSSTSEndpoint].(string); ok && v != "" {
|
||||
config.STSEndpoint = v
|
||||
config.STSEndpointResolver = &customSTSEndpointResolver{endpointURL: v}
|
||||
}
|
||||
if v, ok := l.params[consts.FieldAWSIAMEndpoint].(string); ok && v != "" {
|
||||
config.IAMEndpoint = v
|
||||
config.IAMEndpointResolver = &customIAMEndpointResolver{endpointURL: v}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// generateLoginData generates the necessary login data for Vault AWS authentication
|
||||
// by creating a presigned STS GetCallerIdentity request.
|
||||
func generateLoginData(ctx context.Context, awsConfig *aws.Config, headerValue string, logger hclog.Logger) (map[string]interface{}, error) {
|
||||
const iamServerIdHeader = "X-Vault-AWS-IAM-Server-ID"
|
||||
|
||||
loginData := make(map[string]interface{})
|
||||
|
||||
// Validate credentials are available before attempting presign
|
||||
// This catches configuration errors earlier with a clearer error message
|
||||
if _, err := awsConfig.Credentials.Retrieve(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve AWS credentials: %w", err)
|
||||
}
|
||||
|
||||
// If a header value is provided, we need to add it to the signed request
|
||||
// We'll do this by adding middleware to the config
|
||||
if headerValue != "" {
|
||||
awsConfig.APIOptions = append(awsConfig.APIOptions, func(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(middleware.BuildMiddlewareFunc(
|
||||
"AddVaultHeader",
|
||||
func(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (middleware.BuildOutput, middleware.Metadata, error) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if ok {
|
||||
req.Header.Add(iamServerIdHeader, headerValue)
|
||||
}
|
||||
return next.HandleBuild(ctx, in)
|
||||
},
|
||||
), middleware.After)
|
||||
})
|
||||
}
|
||||
|
||||
// Create STS client with awsConfig (which already contains the correct credentials)
|
||||
stsClient := sts.NewFromConfig(*awsConfig)
|
||||
|
||||
// Create presigner - credentials will be retrieved automatically during presigning
|
||||
presignClient := sts.NewPresignClient(stsClient)
|
||||
|
||||
// Presign the GetCallerIdentity request
|
||||
presignedReq, err := presignClient.PresignGetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to presign GetCallerIdentity request: %w", err)
|
||||
}
|
||||
|
||||
// Convert the signed headers map to http.Header for proper marshaling
|
||||
headers := make(http.Header)
|
||||
for k, v := range presignedReq.SignedHeader {
|
||||
headers[k] = v
|
||||
}
|
||||
|
||||
// Marshal headers to JSON
|
||||
headersJson, err := json.Marshal(headers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request headers: %w", err)
|
||||
}
|
||||
|
||||
// Populate login data with base64-encoded values
|
||||
// Note: GetCallerIdentity is a POST request with an empty body
|
||||
loginData[consts.FieldIAMHttpRequestMethod] = presignedReq.Method
|
||||
loginData[consts.FieldIAMRequestURL] = base64.StdEncoding.EncodeToString([]byte(presignedReq.URL))
|
||||
loginData[consts.FieldIAMRequestHeaders] = base64.StdEncoding.EncodeToString(headersJson)
|
||||
loginData[consts.FieldIAMRequestBody] = base64.StdEncoding.EncodeToString([]byte(""))
|
||||
|
||||
return loginData, nil
|
||||
}
|
||||
|
||||
// signAWSLogin is for use by the generic auth method
|
||||
func signAWSLogin(parameters map[string]interface{}, logger hclog.Logger) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var accessKey string
|
||||
if v, ok := parameters[consts.FieldAWSAccessKeyID].(string); ok {
|
||||
accessKey = v
|
||||
|
|
@ -331,7 +497,13 @@ func signAWSLogin(parameters map[string]interface{}, logger hclog.Logger) error
|
|||
sessionToken = v
|
||||
}
|
||||
|
||||
creds, err := awsutil.RetrieveCreds(accessKey, secretKey, sessionToken, logger)
|
||||
var region string
|
||||
if v, ok := parameters["sts_region"].(string); ok {
|
||||
region = v
|
||||
}
|
||||
|
||||
awsConfig, err := awsutil.RetrieveCreds(ctx, accessKey, secretKey, sessionToken, logger,
|
||||
awsutil.WithRegion(region))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve AWS credentials: %s", err)
|
||||
}
|
||||
|
|
@ -341,12 +513,7 @@ func signAWSLogin(parameters map[string]interface{}, logger hclog.Logger) error
|
|||
headerValue = v
|
||||
}
|
||||
|
||||
var stsRegion string
|
||||
if v, ok := parameters["sts_region"].(string); ok {
|
||||
stsRegion = v
|
||||
}
|
||||
|
||||
loginData, err := awsutil.GenerateLoginData(creds, headerValue, stsRegion, logger)
|
||||
loginData, err := generateLoginData(ctx, awsConfig, headerValue, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate AWS login data: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/vault/api"
|
||||
|
||||
|
|
@ -188,6 +188,50 @@ func TestAuthLoginAWS_getCredentialsConfig(t *testing.T) {
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-creds-with-role-arn",
|
||||
fields: fields{
|
||||
AuthLoginCommon: AuthLoginCommon{
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
consts.FieldAWSRoleSessionName: "test-session",
|
||||
},
|
||||
},
|
||||
},
|
||||
logger: hclog.NewNullLogger(),
|
||||
want: &awsutil.CredentialsConfig{
|
||||
Region: "us-east-1",
|
||||
AccessKey: "key-id",
|
||||
SecretKey: "secret-key",
|
||||
RoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
RoleSessionName: "test-session",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "static-creds-with-session-token-and-role-arn",
|
||||
fields: fields{
|
||||
AuthLoginCommon: AuthLoginCommon{
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSSessionToken: "session-token",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
},
|
||||
},
|
||||
},
|
||||
logger: hclog.NewNullLogger(),
|
||||
want: &awsutil.CredentialsConfig{
|
||||
Region: "us-east-1",
|
||||
AccessKey: "key-id",
|
||||
SecretKey: "secret-key",
|
||||
SessionToken: "session-token",
|
||||
RoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "all",
|
||||
fields: fields{
|
||||
|
|
@ -209,11 +253,11 @@ func TestAuthLoginAWS_getCredentialsConfig(t *testing.T) {
|
|||
},
|
||||
logger: hclog.NewNullLogger(),
|
||||
want: &awsutil.CredentialsConfig{
|
||||
AccessKey: "key-id",
|
||||
SecretKey: "sa-key",
|
||||
SessionToken: "session-token",
|
||||
IAMEndpoint: "iam.us-east-2.amazonaws.com",
|
||||
STSEndpoint: "sts.us-east-2.amazonaws.com",
|
||||
AccessKey: "key-id",
|
||||
SecretKey: "sa-key",
|
||||
SessionToken: "session-token",
|
||||
// Note: IAMEndpoint and STSEndpoint have been replaced with endpoint resolvers in v2
|
||||
// and are no longer simple string fields
|
||||
Region: "us-east-2",
|
||||
Filename: "credentials",
|
||||
Profile: "profile1",
|
||||
|
|
@ -238,8 +282,41 @@ func TestAuthLoginAWS_getCredentialsConfig(t *testing.T) {
|
|||
if got.HTTPClient == nil {
|
||||
t.Errorf("getCredentialsConfig() HTTPClient not initialized")
|
||||
}
|
||||
// set HTTPClient to nil
|
||||
|
||||
// Verify custom endpoint resolvers are set correctly when endpoints are provided
|
||||
// We check these explicitly because they're interfaces and can't be compared with DeepEqual
|
||||
if stsEndpoint, ok := tt.fields.AuthLoginCommon.params[consts.FieldAWSSTSEndpoint].(string); ok && stsEndpoint != "" {
|
||||
if got.STSEndpointResolver == nil {
|
||||
t.Error("Expected STSEndpointResolver to be set when aws_sts_endpoint is provided")
|
||||
} else {
|
||||
resolver, ok := got.STSEndpointResolver.(*customSTSEndpointResolver)
|
||||
if !ok {
|
||||
t.Error("STSEndpointResolver is not of type *customSTSEndpointResolver")
|
||||
} else if resolver.endpointURL != stsEndpoint {
|
||||
t.Errorf("STSEndpointResolver endpointURL = %v, want %v", resolver.endpointURL, stsEndpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if iamEndpoint, ok := tt.fields.AuthLoginCommon.params[consts.FieldAWSIAMEndpoint].(string); ok && iamEndpoint != "" {
|
||||
if got.IAMEndpointResolver == nil {
|
||||
t.Error("Expected IAMEndpointResolver to be set when aws_iam_endpoint is provided")
|
||||
} else {
|
||||
resolver, ok := got.IAMEndpointResolver.(*customIAMEndpointResolver)
|
||||
if !ok {
|
||||
t.Error("IAMEndpointResolver is not of type *customIAMEndpointResolver")
|
||||
} else if resolver.endpointURL != iamEndpoint {
|
||||
t.Errorf("IAMEndpointResolver endpointURL = %v, want %v", resolver.endpointURL, iamEndpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set HTTPClient, Logger, and endpoint resolvers to nil before DeepEqual comparison
|
||||
// We've already verified endpoint resolvers above
|
||||
got.HTTPClient = nil
|
||||
got.Logger = nil
|
||||
got.STSEndpointResolver = nil
|
||||
got.IAMEndpointResolver = nil
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("getCredentialsConfig() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
|
@ -247,6 +324,262 @@ func TestAuthLoginAWS_getCredentialsConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestAuthLoginAWS_RoleAssumption tests the manual role assumption logic
|
||||
func TestAuthLoginAWS_RoleAssumption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectRoleAssumption bool
|
||||
expectSessionName string
|
||||
}{
|
||||
{
|
||||
name: "with-role-arn-and-session-name",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
consts.FieldAWSRoleSessionName: "custom-session",
|
||||
},
|
||||
expectRoleAssumption: true,
|
||||
expectSessionName: "custom-session",
|
||||
},
|
||||
{
|
||||
name: "with-role-arn-default-session-name",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
},
|
||||
expectRoleAssumption: true,
|
||||
expectSessionName: "", // Session name is not set in config when not provided
|
||||
},
|
||||
{
|
||||
name: "without-role-arn",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
},
|
||||
expectRoleAssumption: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := &AuthLoginAWS{
|
||||
AuthLoginCommon: AuthLoginCommon{
|
||||
params: tt.params,
|
||||
},
|
||||
}
|
||||
|
||||
// Verify that getCredentialsConfig properly sets role ARN
|
||||
config, err := l.getCredentialsConfig(hclog.NewNullLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("getCredentialsConfig() error = %v", err)
|
||||
}
|
||||
|
||||
if tt.expectRoleAssumption {
|
||||
if config.RoleARN == "" {
|
||||
t.Errorf("Expected RoleARN to be set, got empty string")
|
||||
}
|
||||
if roleARN, ok := tt.params[consts.FieldAWSRoleARN].(string); ok {
|
||||
if config.RoleARN != roleARN {
|
||||
t.Errorf("Expected RoleARN = %v, got %v", roleARN, config.RoleARN)
|
||||
}
|
||||
}
|
||||
|
||||
// Check session name
|
||||
expectedSessionName := tt.expectSessionName
|
||||
if config.RoleSessionName != expectedSessionName {
|
||||
t.Errorf("Expected RoleSessionName = %v, got %v", expectedSessionName, config.RoleSessionName)
|
||||
}
|
||||
} else {
|
||||
if config.RoleARN != "" {
|
||||
t.Errorf("Expected RoleARN to be empty, got %v", config.RoleARN)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthLoginAWS_SessionTokenWithRoleARN tests that session token is handled correctly with role assumption
|
||||
func TestAuthLoginAWS_SessionTokenWithRoleARN(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectSessionTokenSet bool
|
||||
}{
|
||||
{
|
||||
name: "session-token-without-role-arn",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSSessionToken: "session-token",
|
||||
},
|
||||
expectSessionTokenSet: true,
|
||||
},
|
||||
{
|
||||
name: "session-token-with-role-arn",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSSessionToken: "session-token",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
},
|
||||
expectSessionTokenSet: true,
|
||||
},
|
||||
{
|
||||
name: "no-session-token-with-role-arn",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSRoleARN: "arn:aws:iam::123456789012:role/test-role",
|
||||
},
|
||||
expectSessionTokenSet: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := &AuthLoginAWS{
|
||||
AuthLoginCommon: AuthLoginCommon{
|
||||
params: tt.params,
|
||||
},
|
||||
}
|
||||
|
||||
config, err := l.getCredentialsConfig(hclog.NewNullLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("getCredentialsConfig() error = %v", err)
|
||||
}
|
||||
|
||||
hasSessionToken := config.SessionToken != ""
|
||||
if hasSessionToken != tt.expectSessionTokenSet {
|
||||
t.Errorf("Expected SessionToken set = %v, got %v", tt.expectSessionTokenSet, hasSessionToken)
|
||||
}
|
||||
|
||||
// Verify session token value matches if it should be set
|
||||
if tt.expectSessionTokenSet {
|
||||
if sessionToken, ok := tt.params[consts.FieldAWSSessionToken].(string); ok {
|
||||
if config.SessionToken != sessionToken {
|
||||
t.Errorf("Expected SessionToken = %v, got %v", sessionToken, config.SessionToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthLoginAWS_CustomEndpoints tests that custom STS and IAM endpoints are properly configured
|
||||
func TestAuthLoginAWS_CustomEndpoints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectSTSEndpoint bool
|
||||
expectIAMEndpoint bool
|
||||
stsEndpoint string
|
||||
iamEndpoint string
|
||||
}{
|
||||
{
|
||||
name: "with-custom-sts-endpoint",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSSTSEndpoint: "https://sts.custom.endpoint.com",
|
||||
},
|
||||
expectSTSEndpoint: true,
|
||||
expectIAMEndpoint: false,
|
||||
stsEndpoint: "https://sts.custom.endpoint.com",
|
||||
},
|
||||
{
|
||||
name: "with-custom-iam-endpoint",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSIAMEndpoint: "https://iam.custom.endpoint.com",
|
||||
},
|
||||
expectSTSEndpoint: false,
|
||||
expectIAMEndpoint: true,
|
||||
iamEndpoint: "https://iam.custom.endpoint.com",
|
||||
},
|
||||
{
|
||||
name: "with-both-custom-endpoints",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
consts.FieldAWSSTSEndpoint: "https://sts.custom.endpoint.com",
|
||||
consts.FieldAWSIAMEndpoint: "https://iam.custom.endpoint.com",
|
||||
},
|
||||
expectSTSEndpoint: true,
|
||||
expectIAMEndpoint: true,
|
||||
stsEndpoint: "https://sts.custom.endpoint.com",
|
||||
iamEndpoint: "https://iam.custom.endpoint.com",
|
||||
},
|
||||
{
|
||||
name: "without-custom-endpoints",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldAWSAccessKeyID: "key-id",
|
||||
consts.FieldAWSSecretAccessKey: "secret-key",
|
||||
},
|
||||
expectSTSEndpoint: false,
|
||||
expectIAMEndpoint: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := &AuthLoginAWS{
|
||||
AuthLoginCommon: AuthLoginCommon{
|
||||
params: tt.params,
|
||||
},
|
||||
}
|
||||
|
||||
logger := hclog.NewNullLogger()
|
||||
config, err := l.getCredentialsConfig(logger)
|
||||
if err != nil {
|
||||
t.Errorf("getCredentialsConfig() unexpected error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check STS endpoint resolver
|
||||
if tt.expectSTSEndpoint {
|
||||
if config.STSEndpointResolver == nil {
|
||||
t.Error("Expected STSEndpointResolver to be set, but it was nil")
|
||||
} else {
|
||||
// Verify the resolver returns the correct endpoint
|
||||
resolver, ok := config.STSEndpointResolver.(*customSTSEndpointResolver)
|
||||
if !ok {
|
||||
t.Errorf("STSEndpointResolver is not of type *customSTSEndpointResolver")
|
||||
} else if resolver.endpointURL != tt.stsEndpoint {
|
||||
t.Errorf("STSEndpointResolver endpointURL = %v, want %v", resolver.endpointURL, tt.stsEndpoint)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if config.STSEndpointResolver != nil {
|
||||
t.Error("Expected STSEndpointResolver to be nil, but it was set")
|
||||
}
|
||||
}
|
||||
|
||||
// Check IAM endpoint resolver
|
||||
if tt.expectIAMEndpoint {
|
||||
if config.IAMEndpointResolver == nil {
|
||||
t.Error("Expected IAMEndpointResolver to be set, but it was nil")
|
||||
} else {
|
||||
// Verify the resolver returns the correct endpoint
|
||||
resolver, ok := config.IAMEndpointResolver.(*customIAMEndpointResolver)
|
||||
if !ok {
|
||||
t.Errorf("IAMEndpointResolver is not of type *customIAMEndpointResolver")
|
||||
} else if resolver.endpointURL != tt.iamEndpoint {
|
||||
t.Errorf("IAMEndpointResolver endpointURL = %v, want %v", resolver.endpointURL, tt.iamEndpoint)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if config.IAMEndpointResolver != nil {
|
||||
t.Error("Expected IAMEndpointResolver to be nil, but it was set")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthLoginAWS_Login(t *testing.T) {
|
||||
handlerFunc := func(t *testLoginHandler, w http.ResponseWriter, req *http.Request) {
|
||||
role := "default"
|
||||
|
|
@ -295,7 +628,9 @@ func TestAuthLoginAWS_Login(t *testing.T) {
|
|||
authField: "baz",
|
||||
mount: "foo",
|
||||
params: map[string]interface{}{
|
||||
consts.FieldRole: "bob",
|
||||
consts.FieldRole: "bob",
|
||||
consts.FieldAWSAccessKeyID: "test-key",
|
||||
consts.FieldAWSSecretAccessKey: "test-secret",
|
||||
},
|
||||
initialized: true,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue