diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3de68e304..0e6f08d8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,9 +65,9 @@ jobs: image: - "vault-enterprise:1.14.13-ent" - "vault-enterprise:1.15.16-ent" - - "vault-enterprise:1.16.11-ent" - - "vault-enterprise:1.17.7-ent" - - "vault-enterprise:1.18.0-ent" + - "vault-enterprise:1.16.12-ent" + - "vault-enterprise:1.17.8-ent" + - "vault-enterprise:1.18.1-ent" - "vault:latest" services: vault: diff --git a/.go-version b/.go-version index 89144dbc3..d28b1eb8f 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.22.3 +1.22.9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f78baf10..107ec887b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,27 @@ FEATURES: +* Update `vault_database_secret_backend_connection`to support `password_authentication` for PostgreSQL, allowing to encrypt password before being passed to PostgreSQL ([#2371](https://github.com/hashicorp/terraform-provider-vault/pull/2371)) +* Add support for `external_id` field for the `vault_aws_auth_backend_sts_role` resource ([#2370](https://github.com/hashicorp/terraform-provider-vault/pull/2370)) + +## 4.5.0 (Nov 19, 2024) + +FEATURES: + * Update `vault_database_secret_backend_connection` to support inline TLS config for PostgreSQL ([#2339](https://github.com/hashicorp/terraform-provider-vault/pull/2339)) * Update `vault_database_secret_backend_connection` to support skip_verification config for Cassandra ([#2346](https://github.com/hashicorp/terraform-provider-vault/pull/2346)) * Update `vault_approle_auth_backend_role_secret_id` to support `num_uses` and `ttl` fields ([#2345](https://github.com/hashicorp/terraform-provider-vault/pull/2345)) -* Add support for `use_annotations_as_alias_metadata` field for the `vault_kubernetes_auth_backend_config` resource ([#2206](https://github.com/hashicorp/terraform-provider-vault/pull/2206)) * Add support for `allow_empty_principals` field for the `vault_ssh_secret_backend_role` resource ([#2354](https://github.com/hashicorp/terraform-provider-vault/pull/2354)) * Update `vault_gcp_secret_impersonated_account` to support setting `ttl` ([#2318](https://github.com/hashicorp/terraform-provider-vault/pull/2318)) * Add support for `connection_timeout` field for the `vault_ldap_auth_backend` resource ([#2358](https://github.com/hashicorp/terraform-provider-vault/pull/2358)) * Add support for Rootless Configuration for Static Roles to Postgres DB ([#2341](https://github.com/hashicorp/terraform-provider-vault/pull/2341)) +* Add support for `use_annotations_as_alias_metadata` field for the `vault_kubernetes_auth_backend_config` resource ([#2226](https://github.com/hashicorp/terraform-provider-vault/pull/2226)) * Add new data source and resource `vault_pki_secret_backend_config_cmpv2`. Requires Vault 1.18+. *Available only for Vault Enterprise* ([#2330](https://github.com/hashicorp/terraform-provider-vault/pull/2330)) +BUGS: + +* Remove consul secret backend role from state if not found on vault: ([#2321](https://github.com/hashicorp/terraform-provider-vault/pull/2321)) + ## 4.4.0 (Aug 7, 2024) FEATURES: diff --git a/CODEOWNERS b/CODEOWNERS index a765f7ea9..7476e1d8a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @hashicorp/vault-ecosystem +* @hashicorp/vault diff --git a/vault/resource_aws_auth_backend_sts_role.go b/vault/resource_aws_auth_backend_sts_role.go index b79bf35f8..96e481eb5 100644 --- a/vault/resource_aws_auth_backend_sts_role.go +++ b/vault/resource_aws_auth_backend_sts_role.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" ) @@ -52,6 +53,11 @@ func awsAuthBackendSTSRoleResource() *schema.Resource { return strings.Trim(v.(string), "/") }, }, + consts.FieldExternalID: { + Type: schema.TypeString, + Optional: true, + Description: "External ID expected by the STS role.", + }, }, } } @@ -65,13 +71,20 @@ func awsAuthBackendSTSRoleCreate(d *schema.ResourceData, meta interface{}) error backend := d.Get("backend").(string) accountID := d.Get("account_id").(string) stsRole := d.Get("sts_role").(string) + externalID := d.Get(consts.FieldExternalID).(string) path := awsAuthBackendSTSRolePath(backend, accountID) - log.Printf("[DEBUG] Writing STS role %q to AWS auth backend", path) - _, err := client.Logical().Write(path, map[string]interface{}{ + data := map[string]interface{}{ "sts_role": stsRole, - }) + } + + if provider.IsAPISupported(meta, provider.VaultVersion117) { + data[consts.FieldExternalID] = externalID + } + + log.Printf("[DEBUG] Writing STS role %q to AWS auth backend", path) + _, err := client.Logical().Write(path, data) d.SetId(path) @@ -117,6 +130,15 @@ func awsAuthBackendSTSRoleRead(d *schema.ResourceData, meta interface{}) error { d.Set("backend", backend) d.Set("account_id", accountID) d.Set("sts_role", resp.Data["sts_role"]) + + if provider.IsAPISupported(meta, provider.VaultVersion117) { + if v, ok := resp.Data[consts.FieldExternalID]; ok { + if err := d.Set(consts.FieldExternalID, v); err != nil { + return err + } + } + } + return nil } @@ -127,12 +149,20 @@ func awsAuthBackendSTSRoleUpdate(d *schema.ResourceData, meta interface{}) error } stsRole := d.Get("sts_role").(string) + externalID := d.Get(consts.FieldExternalID).(string) + path := d.Id() - log.Printf("[DEBUG] Updating STS role %q in AWS auth backend", path) - _, err := client.Logical().Write(path, map[string]interface{}{ + data := map[string]interface{}{ "sts_role": stsRole, - }) + } + + if provider.IsAPISupported(meta, provider.VaultVersion117) { + data[consts.FieldExternalID] = externalID + } + + log.Printf("[DEBUG] Updating STS role %q in AWS auth backend", path) + _, err := client.Logical().Write(path, data) if err != nil { return fmt.Errorf("error updating STS role %q in AWS auth backend", path) } diff --git a/vault/resource_aws_auth_backend_sts_role_test.go b/vault/resource_aws_auth_backend_sts_role_test.go index b331594b6..80d241426 100644 --- a/vault/resource_aws_auth_backend_sts_role_test.go +++ b/vault/resource_aws_auth_backend_sts_role_test.go @@ -6,31 +6,55 @@ package vault import ( "fmt" "strconv" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/testutil" ) -func TestAccAWSAuthBackendSTSRole_import(t *testing.T) { +func TestAccAWSAuthBackendSTSRole_withExternalID(t *testing.T) { backend := acctest.RandomWithPrefix("aws") accountID := strconv.Itoa(acctest.RandInt()) arn := acctest.RandomWithPrefix("arn:aws:iam::" + accountID + ":role/test-role") + externalID := "external-id" + updatedExternalID := "external-id-updated" + resourceName := "vault_aws_auth_backend_sts_role.role" + resource.Test(t, resource.TestCase{ - PreCheck: func() { testutil.TestAccPreCheck(t) }, + PreCheck: func() { + testutil.TestAccPreCheck(t) + SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion117) + }, ProviderFactories: providerFactories, CheckDestroy: testAccCheckAWSAuthBackendSTSRoleDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAuthBackendSTSRoleConfig_basic(backend, accountID, arn), - Check: testAccAWSAuthBackendSTSRoleCheck_attrs(backend, accountID, arn), + Config: testAccAWSAuthBackendSTSRoleConfig(backend, accountID, arn, externalID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "backend", backend), + resource.TestCheckResourceAttr(resourceName, "account_id", accountID), + resource.TestCheckResourceAttr(resourceName, "sts_role", arn), + resource.TestCheckResourceAttr(resourceName, consts.FieldExternalID, externalID), + ), }, { - ResourceName: "vault_aws_auth_backend_sts_role.role", + // Update external ID. + Config: testAccAWSAuthBackendSTSRoleConfig(backend, accountID, arn, updatedExternalID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "backend", backend), + resource.TestCheckResourceAttr(resourceName, "account_id", accountID), + resource.TestCheckResourceAttr(resourceName, "sts_role", arn), + resource.TestCheckResourceAttr(resourceName, consts.FieldExternalID, updatedExternalID), + ), + }, + { + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -49,13 +73,19 @@ func TestAccAWSAuthBackendSTSRole_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAuthBackendSTSRoleDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAuthBackendSTSRoleConfig_basic(backend, accountID, arn), + Config: testAccAWSAuthBackendSTSRoleConfig(backend, accountID, arn, ""), Check: testAccAWSAuthBackendSTSRoleCheck_attrs(backend, accountID, arn), }, { - Config: testAccAWSAuthBackendSTSRoleConfig_basic(backend, accountID, updatedArn), + // Update ARN. + Config: testAccAWSAuthBackendSTSRoleConfig(backend, accountID, updatedArn, ""), Check: testAccAWSAuthBackendSTSRoleCheck_attrs(backend, accountID, updatedArn), }, + { + ResourceName: "vault_aws_auth_backend_sts_role.role", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -129,17 +159,28 @@ func testAccAWSAuthBackendSTSRoleCheck_attrs(backend, accountID, stsRole string) } } -func testAccAWSAuthBackendSTSRoleConfig_basic(backend, accountID, stsRole string) string { - return fmt.Sprintf(` +func testAccAWSAuthBackendSTSRoleConfig(backend, accountID, stsRole, externalID string) string { + backendResource := fmt.Sprintf(` resource "vault_auth_backend" "aws" { - type = "aws" - path = "%s" -} + type = "aws" + path = "%s" +}`, backend) + + roleResourceOptionalFields := "" + if externalID != "" { + roleResourceOptionalFields += fmt.Sprintf(` + external_id = "%s"`, externalID) + } + roleResource := fmt.Sprintf(` resource "vault_aws_auth_backend_sts_role" "role" { - backend = vault_auth_backend.aws.path - account_id = "%s" - sts_role = "%s" + backend = vault_auth_backend.aws.path + account_id = "%s" + sts_role = "%s"%s } -`, backend, accountID, stsRole) +`, accountID, stsRole, roleResourceOptionalFields) + + resources := []string{backendResource, roleResource} + + return strings.Join(resources, "\n") } diff --git a/vault/resource_database_secret_backend_connection.go b/vault/resource_database_secret_backend_connection.go index 50bf82028..d394d5d54 100644 --- a/vault/resource_database_secret_backend_connection.go +++ b/vault/resource_database_secret_backend_connection.go @@ -836,6 +836,12 @@ func postgresConnectionStringResource() *schema.Resource { Optional: true, Description: "If set, allows onboarding static roles with a rootless connection configuration.", } + r.Schema["password_authentication"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "password", + Description: "When set to `scram-sha-256`, passwords will be hashed by Vault before being sent to PostgreSQL.", + } return r } @@ -1150,6 +1156,12 @@ func getPostgresConnectionDetailsFromResponse(d *schema.ResourceData, prefix str } } + if provider.IsAPISupported(meta, provider.VaultVersion114) { + if v, ok := data["password_authentication"]; ok { + result["password_authentication"] = v.(string) + } + } + if provider.IsAPISupported(meta, provider.VaultVersion118) { if v, ok := data["tls_ca"]; ok { result["tls_ca"] = v.(string) @@ -1571,6 +1583,12 @@ func setPostgresDatabaseConnectionData(d *schema.ResourceData, prefix string, da } } + if provider.IsAPISupported(meta, provider.VaultVersion114) { + if v, ok := d.GetOk(prefix + "password_authentication"); ok { + data["password_authentication"] = v.(string) + } + } + if provider.IsAPISupported(meta, provider.VaultVersion118) && provider.IsEnterpriseSupported(meta) { if v, ok := d.GetOk(prefix + "self_managed"); ok { data["self_managed"] = v.(bool) diff --git a/vault/resource_database_secret_backend_connection_test.go b/vault/resource_database_secret_backend_connection_test.go index 69e85fe17..104aec513 100644 --- a/vault/resource_database_secret_backend_connection_test.go +++ b/vault/resource_database_secret_backend_connection_test.go @@ -812,6 +812,7 @@ func TestAccDatabaseSecretBackendConnection_postgresql(t *testing.T) { resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "root_rotation_statements.0", "FOOBAR"), resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "verify_connection", "true"), resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.connection_url", connURL), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.password_authentication", "password"), resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.max_open_connections", maxOpenConnections), resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.max_idle_connections", maxIdleConnections), resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.max_connection_lifetime", maxConnLifetime), @@ -833,6 +834,12 @@ func TestAccDatabaseSecretBackendConnection_postgresql(t *testing.T) { resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.username_template", ""), ), }, + { + Config: testAccDatabaseSecretBackendConnectionConfig_postgresql_password_authentication(name, backend, parsedURL), + Check: testComposeCheckFuncCommonDatabaseSecretBackend(name, backend, pluginName, + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "postgresql.0.password_authentication", "scram-sha-256"), + ), + }, }, }) } @@ -1771,6 +1778,27 @@ resource "vault_database_secret_backend_connection" "test" { `, path, name, parsedURL.String()) } +func testAccDatabaseSecretBackendConnectionConfig_postgresql_password_authentication(name, path string, parsedURL *url.URL) string { + return fmt.Sprintf(` +resource "vault_mount" "db" { + path = "%s" + type = "database" +} + +resource "vault_database_secret_backend_connection" "test" { + backend = vault_mount.db.path + name = "%s" + allowed_roles = ["dev", "prod"] + root_rotation_statements = ["FOOBAR"] + + postgresql { + connection_url = "%s" + password_authentication = "scram-sha-256" + } +} +`, path, name, parsedURL.String()) +} + func testAccDatabaseSecretBackendConnectionConfig_postgresql_tls(name, path, tlsCA, tlsCert, privateKey string) string { return fmt.Sprintf(` resource "vault_mount" "db" { @@ -1849,7 +1877,7 @@ resource "vault_database_secret_backend_connection" "test" { allowed_roles = ["dev", "prod"] root_rotation_statements = ["FOOBAR"] - snowflake { + snowflake { connection_url = "%s" username = "%s" password = "%s" diff --git a/website/docs/r/aws_auth_backend_sts_role.html.md b/website/docs/r/aws_auth_backend_sts_role.html.md index 20535a616..98ac51c09 100644 --- a/website/docs/r/aws_auth_backend_sts_role.html.md +++ b/website/docs/r/aws_auth_backend_sts_role.html.md @@ -51,6 +51,8 @@ The following arguments are supported: * `backend` - (Optional) The path the AWS auth backend being configured was mounted at. Defaults to `aws`. +* `external_id` - (Optional) External ID expected by the STS role. The associated STS role must be configured to require the external ID. Requires Vault 1.17+. + ## Attributes Reference No additional attributes are exported by this resource. diff --git a/website/docs/r/database_secret_backend_connection.md b/website/docs/r/database_secret_backend_connection.md index fab521f1a..eaf6bccbd 100644 --- a/website/docs/r/database_secret_backend_connection.md +++ b/website/docs/r/database_secret_backend_connection.md @@ -124,7 +124,7 @@ Exactly one of the nested blocks of configuration options must be supplied. * `connect_timeout` - (Optional) The number of seconds to use as a connection timeout. -* `skip_verification` - (Optional) Skip permissions checks when a connection to Cassandra is first created. +* `skip_verification` - (Optional) Skip permissions checks when a connection to Cassandra is first created. These checks ensure that Vault is able to create roles, but can be resource intensive in clusters with many roles. ### Couchbase Configuration Options @@ -328,8 +328,8 @@ See the [Vault * `password` - (Optional) The root credential password used in the connection URL. -* `self_managed` - (Optional) If set, allows onboarding static roles with a rootless - connection configuration. Mutually exclusive with `username` and `password`. +* `self_managed` - (Optional) If set, allows onboarding static roles with a rootless + connection configuration. Mutually exclusive with `username` and `password`. If set, will force `verify_connection` to be false. Requires Vault 1.18+ Enterprise. * `tls_ca` - (Optional) The x509 CA file for validating the certificate @@ -338,6 +338,10 @@ See the [Vault * `tls_certificate` - (Optional) The x509 client certificate for connecting to the database. Must be PEM encoded. +* `password_authentication` - (Optional) When set to `scram-sha-256`, passwords will be + hashed by Vault before being sent to PostgreSQL. See the [Vault docs](https://www.vaultproject.io/api-docs/secret/databases/postgresql.html#sample-payload) + for an example. Requires Vault 1.14+. + * `private_key` - (Optional) The secret key used for the x509 client certificate. Must be PEM encoded.