From eb3d91f1f442b51fedd001313576f3bd17280c6f Mon Sep 17 00:00:00 2001 From: Jean-Damien HATZENBUHLER Date: Wed, 28 May 2025 09:51:56 +0200 Subject: [PATCH] feat: support tls_ca and tls_certificate_key for mongodb connections --- CHANGELOG.md | 1 + ...urce_database_secret_backend_connection.go | 66 ++++++++++++++++--- ...database_secret_backend_connection_test.go | 55 ++++++++++++++++ 3 files changed, 114 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeb3d05e8..434b49d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ FEATURES: * Add support for `group_by` and `secondary_rate` on resource `vault_quota_rate_limit`. Requires Vault Enterprise 1.20.0+ ([#2476](https://github.com/hashicorp/terraform-provider-vault/pull/2476)) * Add support for Transit CMAC endpoint ([#2488](https://github.com/hashicorp/terraform-provider-vault/pull/2488)) +* Add support `tls_ca` and `tls_certificate_key` for mongodb connections [#2482](https://github.com/hashicorp/terraform-provider-vault/pull/2482) * Add new resource `vault_scep_auth_backend_role` to manage roles in a SCEP auth backend. [#2479](https://github.com/hashicorp/terraform-provider-vault/pull/2479). * Add new datasource and resource `vault_pki_secret_backend_config_scep` for PKI SCEP configuration. [#2487](https://github.com/hashicorp/terraform-provider-vault/pull/2487). diff --git a/vault/resource_database_secret_backend_connection.go b/vault/resource_database_secret_backend_connection.go index 9003b294c..9faa5d980 100644 --- a/vault/resource_database_secret_backend_connection.go +++ b/vault/resource_database_secret_backend_connection.go @@ -486,12 +486,10 @@ func getDatabaseSchema(typ schema.ValueType) schemaMap { ConflictsWith: util.CalculateConflictsWith(dbEngineInfluxDB.Name(), dbEngineTypes), }, dbEngineMongoDB.name: { - Type: typ, - Optional: true, - Description: "Connection parameters for the mongodb-database-plugin plugin.", - Elem: connectionStringResource(&connectionStringConfig{ - includeUserPass: true, - }), + Type: typ, + Optional: true, + Description: "Connection parameters for the mongodb-database-plugin plugin.", + Elem: mongodbConnectionStringResource(), MaxItems: 1, ConflictsWith: util.CalculateConflictsWith(dbEngineMongoDB.Name(), dbEngineTypes), }, @@ -942,6 +940,23 @@ func snowflakeConnectionStringResource() *schema.Resource { r.Schema[consts.FieldPassword].Deprecated = "Snowflake is ending support for single-factor password authentication " + "by November 2025. Refer to the documentation for more information on migrating to key-pair authentication." + return r +} + +func mongodbConnectionStringResource() *schema.Resource { + r := connectionStringResource(&connectionStringConfig{ + includeUserPass: true, + }) + r.Schema["tls_certificate_key"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "x509 certificate for connecting to the database. This must be a PEM encoded version of the private key and the certificate combined.", + } + r.Schema["tls_ca"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "x509 CA file for validating the certificate presented by the MongoDB server. Must be PEM encoded.", + } return r } @@ -1017,7 +1032,7 @@ func getDatabaseAPIDataForEngine(engine *dbEngine, idx int, d *schema.ResourceDa case dbEngineHana: setDatabaseConnectionDataWithDisableEscaping(d, prefix, data) case dbEngineMongoDB: - setDatabaseConnectionDataWithUserPass(d, prefix, data) + setMongoDBDatabaseConnectionData(d, prefix, data) case dbEngineMongoDBAtlas: setMongoDBAtlasDatabaseConnectionData(d, prefix, data) case dbEngineMSSQL: @@ -1281,6 +1296,31 @@ func getMySQLConnectionDetailsFromResponse(d *schema.ResourceData, prefix string return result } +func getMongoDBConnectionDetailsFromResponse(d *schema.ResourceData, prefix string, resp *api.Secret) map[string]interface{} { + result := getConnectionDetailsFromResponseWithUserPass(d, prefix, resp) + details := resp.Data["connection_details"] + data, ok := details.(map[string]interface{}) + if !ok { + return nil + } + if v, ok := d.GetOk(prefix + "tls_certificate_key"); ok { + result["tls_certificate_key"] = v.(string) + } else { + if v, ok := data["tls_certificate_key"]; ok { + result["tls_certificate_key"] = v.(string) + } + } + if v, ok := d.GetOk(prefix + "tls_ca"); ok { + result["tls_ca"] = v.(string) + } else { + if v, ok := data["tls_ca"]; ok { + result["tls_ca"] = v.(string) + } + } + + return result +} + func getRedisConnectionDetailsFromResponse(d *schema.ResourceData, prefix string, resp *api.Secret) map[string]interface{} { details := resp.Data["connection_details"] data, ok := details.(map[string]interface{}) @@ -1624,6 +1664,16 @@ func setMySQLDatabaseConnectionData(d *schema.ResourceData, prefix string, data } } +func setMongoDBDatabaseConnectionData(d *schema.ResourceData, prefix string, data map[string]interface{}) { + setDatabaseConnectionDataWithUserPass(d, prefix, data) + if v, ok := d.GetOk(prefix + "tls_certificate_key"); ok { + data["tls_certificate_key"] = v.(string) + } + if v, ok := d.GetOk(prefix + "tls_ca"); ok { + data["tls_ca"] = v.(string) + } +} + func setPostgresDatabaseConnectionData(d *schema.ResourceData, prefix string, data map[string]interface{}, meta interface{}) { setDatabaseConnectionDataWithDisableEscaping(d, prefix, data) setCloudDatabaseConnectionData(d, prefix, data, meta) @@ -2138,7 +2188,7 @@ func getDBConnectionConfig(d *schema.ResourceData, engine *dbEngine, idx int, case dbEngineHana: result = getConnectionDetailsFromResponseWithDisableEscaping(d, prefix, resp) case dbEngineMongoDB: - result = getConnectionDetailsFromResponseWithUserPass(d, prefix, resp) + result = getMongoDBConnectionDetailsFromResponse(d, prefix, resp) case dbEngineMongoDBAtlas: result = getConnectionDetailsMongoDBAtlas(d, prefix, resp) case dbEngineMSSQL: diff --git a/vault/resource_database_secret_backend_connection_test.go b/vault/resource_database_secret_backend_connection_test.go index f6b820588..f97295f9c 100644 --- a/vault/resource_database_secret_backend_connection_test.go +++ b/vault/resource_database_secret_backend_connection_test.go @@ -379,6 +379,38 @@ func TestAccDatabaseSecretBackendConnection_mongodb(t *testing.T) { }) } +func TestAccDatabaseSecretBackendConnection_mongodb_tls(t *testing.T) { + MaybeSkipDBTests(t, dbEngineMongoDB) + + // TODO: make these fatal once we auto provision the required test infrastructure. + values := testutil.SkipTestEnvUnset(t, "MONGODB_CA", "MONGODB_URL") + tlsCa, connURL := values[0], values[1] + + backend := acctest.RandomWithPrefix("tf-test-db") + pluginName := dbEngineMongoDB.DefaultPluginName() + name := acctest.RandomWithPrefix("db") + resource.Test(t, resource.TestCase{ + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(context.Background(), t), + PreCheck: func() { testutil.TestAccPreCheck(t) }, + CheckDestroy: testAccDatabaseSecretBackendConnectionCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDatabaseSecretBackendConnectionConfig_mongodb_tls(name, backend, connURL, tlsCa), + Check: testComposeCheckFuncCommonDatabaseSecretBackend(name, backend, pluginName, + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "allowed_roles.#", "2"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "allowed_roles.0", "dev"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "allowed_roles.1", "prod"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "root_rotation_statements.#", "1"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "root_rotation_statements.0", "FOOBAR"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "verify_connection", "true"), + resource.TestCheckResourceAttr(testDefaultDatabaseSecretBackendResource, "mongodb.0.connection_url", connURL), + resource.TestCheckResourceAttr("vault_database_secret_backend_connection.test", "mongodb.0.tls_ca", tlsCa+"\n"), + ), + }, + }, + }) +} + func TestAccDatabaseSecretBackendConnection_mssql(t *testing.T) { MaybeSkipDBTests(t, dbEngineMSSQL) @@ -1701,6 +1733,29 @@ resource "vault_database_secret_backend_connection" "test" { `, path, name, connURL) } +func testAccDatabaseSecretBackendConnectionConfig_mongodb_tls(name, path, connURL, tlsCa string) 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"] + + mongodb { + connection_url = "%s" + tls_ca = <