Manage MongoDB Atlas Database Secrets in HashiCorp Vault

Melissa Plunkett

#partners

Exciting news from HashiCorp via live stream that Vault 1.4 is generally available as of April 7, 2020. This is particularly exciting for us as MongoDB Atlas is now supported in two ways within HashiCorp Vault:

The MongoDB Atlas Database Users Vault Secrets Engine generates unique, ephemeral database users for MongoDB Atlas projects, which can be managed programmatically in Vault. The database secrets engine already supported self-managed MongoDB users, but we extended it to support MongoDB Atlas customers as well.

The MongoDB Atlas Vault Secrets Engine generates unique, ephemeral programmatic API keys for MongoDB Atlas with assigned roles and permissions, along with IP whitelists to restrict individual key access. Project IP whitelists in Atlas do not cover API keys, so each API key must have an associated IP whitelist.

MongoDB Atlas Database Secret Lifecycle

I’ve always found the best way to learn something new is just to try it out, so below we have a short guide that will walk you through the basics of using both of these secrets engines. But before we jump into some hands-on-keyboard fun, there are a few prerequisites you’ll need to knock out.

Ready? Okay, let’s create some MongoDB Atlas secrets!

Managing the Lifecycle of a MongoDB Atlas Database User with HashiCorp Vault

First, you need to enable the secrets engine you want to use. In this case, it’s database as the MongoDB Atlas Vault integration is an extension of this existing secrets engine.

$ vault secrets enable database
Success! Enabled the database secrets engine at: database/

Next, you have to configure the secrets engine for the MongoDB Atlas plugin, so be sure to give the configuration a name that makes sense for your team. Our example uses “acme-mongodbatlas-database” as acme is the code-name of the application. A configuration requires the name of the specific database plugin, the name(s) of Vault roles you will be creating for this project, the public and private programmatic Atlas API key that have sufficient permissions to create users in the project, and the project ID of the project in which you want the users to exist.

$ vault write database/config/acme-mongodbatlas-database \
  plugin_name=mongodbatlas-database-plugin \
  allowed_roles="acme-read-role, acme-dba-role" \
  public_key="<PRIKEY>" \
  private_key="<PUBKEY>" \
  project_id="4e336bb981299e77v6b9229d"

As long as the command returns without an error you are ready to proceed with creating your first Vault role. Tip: You can double-check the config was written by using the vault list command.

In our config example above, we defined two Vault roles in our allowed_roles parameter: acme-read-role and acme-dba-role. As the names imply, we are going to create a Vault role for Atlas that will create a database user for our “acme” application in the denoted project that is restricted to only being able to read the “ecomm” database, and a second Vault role for Atlas that will create a database user in our project that has DBA-level permissions on only the “ecomm” database.

You can now “write” the Vault roles that define your Atlas database users:

$ vault write database/roles/acme-read-role \
  db_name=acme-mongodbatlas-database \
  creation_statements='{ "database_name": "admin", "roles": [{"databaseName":"ecomm","roleName":"read"}]}' \
  default_ttl="1h" \
  max_ttl="24h"
Success! Data written to: database/roles/acme-read-role

$ vault write database/roles/acme-dba-role \
  db_name=acme-mongodbatlas-database \
  creation_statements='{"database_name": "admin", "roles":[{"databaseName":"ecomm","roleName":"dbAdmin"}]}' \
  default_ttl="1h" \
  max_ttl="24h"
Success! Data written to: database/roles/acme-dba-role

Note the “ecomm” database does not have to exist! In Atlas, you can create role-based access controls for database users for databases and collections you have not yet created, which gives you maximum flexibility in the order of operations.

This is also where you can define the lifecycle parameters for your Atlas database users’ lease. These Vault roles have a default time-to-live (TTL) of 1 hour with the ability to renew for up to 24 hours. Once the TTL is reached, the credential is revoked (i.e., deleted) from Atlas. If you do not set a default and max TTL, the Vault defaults will be used.

Now you get to create Atlas database users with a super simple one-liner. Just simply “read” one of the roles you created and in return, an Atlas database user is created. Vault returns the username and password, confirmation on the lease TTL values, and a lease ID. Later, we’ll use the lease ID as the token to further manage the lifecycle of the secret generated.

$ vault read database/creds/acme-read-role
  Key            	  Value
  ---               -----
  lease_id          database/creds/acme-read-role/fHAatgxsxeDVFhakTI1aSnmn
  lease_duration 	  1h
  lease_renewable	  true
  password       	  <PASSWORD>
  username       	  v-root-acme-read-role-dfoDwfXaQCz9SlWuJyL7-1583409850

$ vault read database/creds/acme-dba-role
  Key            	  Value
  ---            	  -----
  lease_id       	  database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS
  lease_duration 	  1h
  lease_renewable	  true
  password       	  <PASSWORD>
  username       	  v-root-acme-dba-role-w1UV9Onih9rKVmLClxGL-1583409983

We can check the Atlas UI to confirm that our new database users have been created. Yup, our users are there:

Database Access User List

Modifying Vault Leases for a MongoDB Atlas Database User

After talking with your team perhaps you all realize DBAs actually need 96 hours as a maximum TTL value, so you want to modify the lease. Let’s try to extend that using the lease ID returned from when we created a DBA user:

$ vault lease renew -increment=96h database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS

WARNING! The following warnings were returned from Vault:

  * TTL of "96h" exceeded the effective max_ttl of "23h41m39s"; TTL value is
  capped accordingly

Key            	Value
---            	-----
lease_id       	database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS
lease_duration 	23h41m39s
lease_renewable	true

Looks like we get a warning that we can’t do that because the maximum TTL was set to 24 hours. If we want to have a DBA role with a max TTL of 96 hours, we’ll need to create a new Vault role. For now, let’s just extend the lease duration to 2 hours (this time doing so in seconds) for our DBA to use:

$ vault lease renew -increment=7200 database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS
Key            	Value
---            	-----
lease_id       	database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS
lease_duration 	2h
lease_renewable	true

Let’s say it turns out that after an hour our users no longer need access. We can cut off access by revoking the lease early, thereby removing the credentials we created:

$ vault lease revoke database/creds/acme-read-role/fHAatgxsxeDVFhakTI1aSnmn
All revocation operations queued successfully!

$ vault lease revoke database/creds/acme-dba-role/sLabhVj25qz1As2DCDWv4EDS
All revocation operations queued successfully!

If you do not revoke before the max TTL then the secret would be removed at that time by Vault.

Managing the Lifecycle of a MongoDB Atlas Programmatic API Key with Vault

First, you need to enable the specific Vault secrets engine we want to use, in this case, mongodbatlas:

$ vault secrets enable mongodbatlas
Success! Enabled the mongodbatlas secrets engine at: mongodbatlas/

Before you can make more programmatic API keys, you need a master programmatic API key with the appropriate roles to make the additional keys. Remember to rotate this key in a way that complies with your security policies.

$ vault write mongodbatlas/config \
  public_key="<PRIKEY>" \
  private_key="<PUBKEY>"
Success! Data written to: mongodbatlas/config

Just like the Vault roles to create database users, you need to write Vault roles to define how you want to create programmatic API keys. In our example, we will first define a role, acme-members, to create an organization-level programmatic API key, with the Atlas role of “ORG_MEMBER”. In order to further ensure security, we will also need to add an IP whitelist entry that contains the IP address of the server where this key will be used, so requests will only succeed if they originate from there. Finally, we will set a default TTL of 2 hours and a maximum of 2 days.

$ vault write mongodbatlas/roles/acme-members \
  organization_id="5b71ff3f99e82140d0aaec14" \
  roles="ORG_MEMBER" \
  ip_addresses="192.168.1.3" \
  ttl="2h" \
  max_ttl="48h"

Let’s define a programmatic key for the project level too, acme-owners, with an Atlas role of “GROUP_OWNER” and include the same IP whitelist entry and TTL values:

$ vault write mongodbatlas/roles/acme-owners \
  project_id="5e224aa679358e09e6b0779d" \
  roles="GROUP_OWNER" \
  ip_addresses="192.168.1.3" \
  ttl="2h" \
  max_ttl="48h"

Now we can create some keys by reading the Vault roles we just created:

$ vault read mongodbatlas/creds/acme-owners
  Key            	  Value
  ---            	  -----
  lease_id       	  mongodbatlas/creds/acme-owners/5YsSGzP8W9pDuRqaU0vlrtg6
  lease_duration 	  2h
  lease_renewable	  true
  description    	  vault-acme-owners-6XwzFAzv3U17Ui0Tb7i0
  private_key    	  <PRIKEY>
  public_key        pvvwjyae

$ vault read mongodbatlas/creds/acme-members
  Key               Value
  ---               -----
  lease_id       	  mongodbatlas/creds/acme-members/ROfMkyhlHhZtFSe9nhvnuBcr
  lease_duration 	  2h
  lease_renewable	  true
  description    	  vault-acme-members-JCZRjAOyRS83RL1VnjPX
  private_key    	  <PRIKEY>
  public_key     	  naxfyjrz

Vault returns the public and private key, confirmation on the lease TTL values, and a lease ID. The description here is stored in Atlas so you know to which Vault role this maps. The lease ID is the token you will use to manage the lifecycle of the secret generated, such as lease renewal or revocation.

Double-checking the Atlas UI, both keys are visible in the organization and only the project-level key in the project:

You can manage the leasing in exactly the same way as with the MongoDB Atlas Database Users Vault Secrets Engine by using the same commands. For example, you can use vault lease renew -increment=4h <lease_id> to change the TTL from 2 hours to 4 hours, and vault lease revoke <lease_id> to revoke (delete) the secret before the TTL is up. Again, if you don’t specify any TTL values, the Vault defaults will be used.

Keys to the Kingdom

You now have the power you need to fully manage your MongoDB Atlas secrets with HashiCorp Vault! Full documentation, code, and issues reporting for the MongoDB Atlas Database Users Secrets Engine and the MongoDB Atlas Secrets Engine are all handled by HashiCorp.

If you have any feature requests for the MongoDB Atlas Vault integrations, please submit them via the MongoDB Feedback Engine and select “Vault plugin” from the category list. Any other topics not covered here are likely found at MongoDB Atlas + HashiCorp Vault, but if you don’t find what you seek feel free to reach out to me directly at melissa.plunkett@mongodb.com.