Intro to Secrets Management using Vault
TL;DR Using short-lived secrets to access a database is much more secure than standard credentials
TL;DR Using short-lived secrets to access a database is much more secure than standard credentials
I recently received my AWS Solutions Architect Associate Cert, and I inadvertently learned more about Ops, DevOps in respect to automation, deployment and most importantly secrets and access. So I wanted to share my knowledge on creating short lived credentials for database users and administrators.
In this blog post, the term Vault will be used to refer to Hashicorp Vault
Please note:
Terminology:
This blog post will cover:
A most common secret is a username and password, but they come in different types, such as an API key. Something that can be used to access a system, or information that requires authorization is considered a secret.
Secrets management is the process of handling secrets securely. This can include how secrets are stored, rotating them and permissions (who can access).
Best practices in short include:
This blog post will cover centralizing secrets, dynamic secrets and auditing
Secrets management is important for multiple reasons:
In this section we will walk through installing Vault.
Official Documentation for installing server is here.
Before we begin, please log into the AWS account (if you are using the Lab setup from the previous post) with the non root user:
.
Move over to the EC2 service and ensure the EC2 instance is running, if it is not, start the instance: .
Please SSH into the instance like so:
ssh -i “demo_key.pem” ubuntu@ec2-3-135-229-44.us-east-2.compute.amazonaws.com
Please note:
Now for installing Vault we need to add the HashiCorp GPG key, the repository and then install Vault.
Select the Ubuntu/Debian tab and follow those instructions:
With the commands below:
1
2
3
4
5
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install vault
Type “vault” in the command line to confirm installation:
Congratulations Vault is installed
We will go through a normal Vault deploy, this includes unsealing Vault.
This is not necessary, there is a Dev server that can be setup quicker. The steps for this can be found here.
For Deploying Vault, the official documentation can be found here
Vault and the configuration will be stored in a vault folder. So according to the documentation create a vault directory with a data older inside like so:
I’ve created it in the Ubuntu user home directory. Just for the tutorial:
1
mkdir -p vault/data
Create the config.hcl file in the home directory, not inside the vault directory:
Open config.hcl, and add the code below:
1
2
3
4
5
6
7
8
9
10
11
12
storage "raft" {
path = "./vault/data"
node_id = "node1"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = "true"
}
api_addr = "http://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"
Now start the server with the config file that has been created.
1
vault server -config=config.hcl
This will start the server and should look like:
The same official documentation for deploying Vault covers this section. Again, it can be found here
Initializing Vault is simply configuring Vault. That is, setting it up.
For this step we need to connect to the EC2 instance in a new terminal window, this is a 2nd connection. The first SSH connection where the server was started should be kept open. In most terminals you can right click and select split horizontally.
So it will look like:
The first thing we need to do is create the VAULT_ADDR environment variable, do so with:
1
export VAULT_ADDR='http://127.0.0.1:8200'
We need to initialize Vault and store the unseal keys and root token. This is done with the operator init
.
Sealing and unsealing is related to Vault being able to access the master key to decrypt the data stored within Vault. In a Sealed state Vault is unable to decrypt data, meaning that in an unsealed state Vault is able to decrypt the data. For more detail on how this works (and it is interesting) read the official documentation here
To continue, in the new terminal session execute:
1
vault operator init
This will give 5 unseal tokens and a root token, please store these in a safe space:
For the tutorial I saved it in good ole NotePad++, but never do that for production!
We need to unseal Vault. To do this, use the unseal command and add 3 valid unseal keys that you saved from the step above.
1
vault operator unseal
Entering unseal keys will look like:
For each successful unseal key the unseal progress will increment by 1.
Once unsealed the Sealed key will be set to false:
On the first terminal where Vault was started, you will see a “post-unseal setup complete” line:
Vault has now been unsealed!
There is a step in the documentation of clean up. Do not do this. If you do, you will have to re-initialize Vault again.
I have made that mistake:
With Vault unsealed, it is a good idea to ensure that we can login. Use the command below to log into Vault:
1
vault login root token
It should present you with information tied to the token, such as the token policies and token itself:
If this works, congratulations you have installed, deployed and logged into a Vault server.
The next steps will be to configure Vault to access the database to create short term credentials.
The default root user created when creating the AWS RDS MySQL Database can be used. But it is preferable to create a separate user that Vault can use. This way, we could audit and log when Vault accesses the database if we wanted (Vault does have its own logs as well, but that is not the topic of this post).
In short this step includes:
AWS Documentation for creating another root user can be found here In order for Vault to create short lived credentials on the database, it needs access to the database.
So first off we have to create a new user on the database with admin credentials, so that Vault can create credentials each time access is requested (a new user and password).
Login to the database from the EC2 server, the command will look like:
1
mysql -h database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com -P 3306 -u admin -p
Once logged in as root, create a new user called ‘vault_user’ and a password with the command:
1
CREATE USER 'vault_user'@'%' IDENTIFIED BY 'vault_password';
For this tutorial the credentials for the account that Vault will use are:
vault_user
with the password vault_password
The new database user needs to be given root permissions. The command to do this is:
1
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO 'vault_user'@'%' WITH GRANT OPTION;
This should have created the new root user vault_user
. The next step will be logging into the database.
Exit the mysql client and attempt to login to the database as the new vault_user
:
If this works, the new user has been created successfully.
Now we have a user that Vault can use to access the database and create new credentials, the next step is to configure Vault to be able to do that.
The main steps will include:
vault_user
we created on the databaseSELECT
permissionsBefore anything can be done to configure Vault, we need to login.
Ensure the environment variable VAULT_ADDR
has been set correctly:
To login use the command vault login <root token>
:
1
vault login <root token>
The root token was given when Vault was initialized and the unseal keys were given.
If successful you will be presented with your token and token information:
After confirming that we are logged in as the root user we can enable the database engine. Vault works on the idea of ‘Secrets Engines’, which I tend to think of as compenents that can be enabled depending on which secret you want to store. There are many to choose from, such as Active Directory, Google Cloud, AWS, and of course Databases. The official documentation for the database secrets engine is here.
The command to enable the database secrets engine is:
1
vault secrets enable database
Success will show that the database secrets engine was enabled:
Official documentation forthe mysql-maria databases can be found here
For Vault to connect to the database, it needs to be configured with the connection information, including the IP/hostname and credentials.
For this, I prefer to have the command on one line, as copy pasting the command does not always allow it to be parsed correct, you may see an error such as:
The command that will work is:
1
vault write database/config/my-mysql-database plugin_name=mysql-database-plugin connection_url=":@tcp(database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com:3306)/" allowed_roles="mysql-role" username="vault_user" password="vault_password"
For further reading into the connection configuration, see the official documentation here
In short the command is including:
The command does not give feedback that it was successful
Information regarding a role to work with the Secrets Database engine can be found here.
A role is created to tell Vault what to do when connecting to the database. In this case the app role will be configured to:
my-mysql-database
from aboveSELECT
permissions
my-mysql-database
mysql-role
Copy pasting the role worked, I have provided it as one line below just in case:
1
vault write database/roles/mysql-role db_name=my-mysql-database creation_statements="CREATE USER ''@'%' IDENTIFIED BY '';GRANT SELECT ON *.* TO ''@'%';" default_ttl="1h" max_ttl="24h"
In the terminal it looks like:
With Vault configured to create short term credentials on the database. We should test that this works.
Vault uses a ‘path’ based system, meaning that we read
the path of the mysql-role
. The role portion of the database engine is not read, the creds
section of the mysql-role
is read.
As such the command to receiving credentials is:
1
vault read database/creds/mysql-role
If this is successful you will be provided with short lived credentials to use on the database:
Now that Vault supplied a username and password, we should test that it works. The same mysqlclient command can be used.
Make sure that the new username and password is used. The mysql command built with the username and password that I was supplied with looks like
1
mysql -h database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com -P 3306 -u vault-root-mysql-role-NX8fngPR5g1XKa -p
As you can see below, the credentials worked! Vault was able to connect to the database with the vault_user
credentials and rotate the password:
If this was successful for you. Congratulations! You have configured Vault to connect to a database and create temporary credentials for a user with SELECT
permissions.
The steps so far have all been created as the root user of Vault. If an organization with multiple employees was using Vault, the would not be sharing the root token between everyone. It is best to have each employee login with their own account, with their own credentials.
This section will walk through:
mysql-role
to get temporary database credentialsVault has the concept of “auth” methods. These are simply components for different types of authentication, such as LDAP and Okta.
In this tutorial the userpass auth method will be used. This is creating a user, whereby logging in involves a username and password.
Official documentation for how the userpass auth method works: here
First off, please ensure that you have exported the root token into the VAULT_Token environment variable:
1
export VAULT_TOKEN="s.3znDXfgnxwqoEQXbMz7eBxiT"
You can double check using echo to print the value of the variable:
Enable the auth method with the command:
1
vault auth enable userpass
Now the userpath auth method has been enabled, we can create a new user.
The below command excludes the policy option, this is to demonstrate that a user may be able to login, but will not be able to access anything unless a policy is attached.
For the tutorial the standard credentials will be used:
username: demo_user
password: demo_password
1
2
vault write auth/userpass/users/demo_user \
password=demo_password
To check that the user is created, you can use the command
1
vault list auth/userpass/users
It will print out a list of users like so:
We are now going to login as the user and attempt to rotate credentials. Because the policy has not been attached, it will not be allowed to read the mysql-role
created in earlier steps.
I have logged into a new session, but this could also be done from the same terminal. If it is a new session, create the VAULT_ADDR enviornment variable again:
1
export VAULT_ADDR='http://127.0.0.1:8200'
The command to login is:
1
vault login -method=userpass username=demo_user password=demo_password
Successfully logging in will print information about the user and related policies.
There is no need to set the VAULT_TOKEN
variable with the new token. It will set it manually. However if you run into the error shown below, use the following command:
1
unset VAULT_TOKEN
As the new user, request to read the mysql-role. This is basically requesting to have temporary credentials to the database created:
1
vault read database/creds/mysql-role
This should result in a permissions denied error:
The next steps will show how to do this correctly, by creating a policy, and then creating a new user (and including the policy).
The standard documentation for policies is here
The simple policy we will create is to allow the new user demo_user
to read the mysql-role
that was created when configuring Vault.
Create a file called read_mysql.hcl
and save the following JSON configuration into it:
Please note:
mysql-role
. This is different to the path of creating a role, which was: vault write database/roles/mysql-role
:1
2
3
4
path "database/creds/mysql-role"
{
capabilities = ["read"]
}
The final result looks like:
This has created the policy file called read_mysql.hcl
, which will be written to Vault, also known as creating the policy. The command to do this is:
1
vault policy write readmysql read_mysql.hcl
If successful it will show that it uploaded the policy:
In the previous steps, the demo_user
was created without the policy assigned, as such the user could not read the mysql-role
credentials.
Now that the policy has been created, when the the user is created, the command will also assign the policy.
I did some reading of the documentation and ‘googling’ but I couldn’t find information to allow a policy to be attached to a user after it had been created. This might be a limitation of the userpass
method.
Create the user correct with:
1
vault write auth/userpass/users/demo_user password=demo_password policies=readmysql
Now the user has been created, the configuration of the policy and attachment to the user can be tesed.
Follow the steps above for logging in. For simplicity, the command to login is below:
1
vault login -method=userpass username=demo_user password=demo_password
Again, if you need to unset the token use the command:
1
unset VAULT_TOKEN
Once logged in the policies listed should be default
and readmysql
.
To confirm this, the command below will list out the policies related to the token being used, as well as user related information.
1
vault token lookup
The user should now be able to read the mysql-role
credentials.
Attempt to read the mysql-role
credentials, or retrieve the credentials with:
1
vault read database/creds/mysql-role
If successful, you will be presented with a username and password for the database that lasts 1 hour:
The last thing to do now, is to test that the credentials generated by Vault do allow the user to log in to the databse.
Use the same login command with the mysql client, but with the new username and password. For the credentials generated in the image above, the command is:
1
mysql -h database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com -P 3306 -u v-userpass-d-mysql-role-F9Tg6an5 -p
This should login to the database:
To confirm which permissions the user has, use the command.:
1
mysql> SHOW GRANTS;
This user can only SELECT everything. Which means they can only read ‘everything’ (wildcard) on anything in the database. An example is that the user cannot create a database:
Congratulations! You have successfully configured Vault to create short lived credentials for a database user. The user also only has SELECT
permissions, which align with the mysql-role
role that was created.
The user above that was created only has read access. It is possible to create a user with more permissions.
The great thing in Vault, is that the database connection my-mysql-database
can have more than one role allowed. The plan is to create a new user with all permissions to one specific database.
To do this, the steps will be:
my-mysql-database
connection configuration again, but allow a second roleIn order for the user to only have all permissions to one database, a database within the AWS MySQL database needs to be created.
Login to the Mysql Database as the vault_user
. The password being vault_password
.
Create a database called demo_db
:
1
CREAT DATABASE demo_db;
A table should be created within the demo_db
database so that the user can insert data.
Select demo_db
with:
1
use demo_db;
Create a table called cars
:
1
2
3
4
5
CREATE TABLE Cars (
ID INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(255),
PRIMARY KEY (ID)
);
Add 1 ‘car’:
1
INSERT INTO Cars (NAME) VALUES ("Toyota");
The table should look like this:
The database configuration my-mysql-database
will be created again to allow a second role called cars-db-role
.
The command with the additional role is:
1
2
3
4
5
vault write database/config/my-mysql-database \
plugin_name=mysql-database-plugin \ connection_url=":@tcp(database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com:3306)/" \
allowed_roles="mysql-role","cars-db-role" \
username="vault_user" \
password="vault_password"
The cars-db-role
role needs to be created, this role will allow a user to have all permissions to the demo_db database.
The command to do this is:
1
vault write database/roles/cars-db-role db_name=my-mysql-database creation_statements="CREATE USER ''@'%' IDENTIFIED BY '';GRANT ALL PRIVILEGES ON demo_db.* TO ''@'%';" default_ttl="1h" max_ttl="24h"
The key difference in the creation statement is the granting of privileges, to allow Vault to create a user that has all privileges on the demo_db
database.
1
GRANT ALL PRIVILEGES ON demo_db.* TO ''@'%'
The my-mysql-database
database configuration and cars-db-role
should look like:
It is a good idea to test the configuration for the second role is correct. As the root user in Vault, retrieve the credentials with:
To get the credentials use the read command again:
1
vault read database/creds/cars-db-role
Login to the database with the mysql client and list the grants with:
SHOW GRANTS;
This will show that the user has grants on the demo_db
database.
Congratulations. You have created two mysql roles that create short term credentials that have different permissions.
This is great for having multiple users and separate permissions based on their job roles.
As above when creating a user, the first step is to create a policy. This will allow the policy to be applied during user creation.
Simply using the commands from the policy creation section and using the vault database/creds/cars-db-role
path we can create a file called car_mysql.hcl
and then create the policy called crud_car_db
The policy file will be:
1
2
3
4
path "database/creds/cars-db-role"
{
capabilities = ["read"]
}
To create a policy within Vault from the car_mysql.hc
policy file is:
1
vault policy write crud_car_db car_mysql.hcl
Those commands should look like:
As the idea was to have two users with different permissions, the second user demo_user
should be created with the crud_car_db
policy applied. The command is:
1
vault write auth/userpass/users/car_user password=car_password \ policies=crud_car_db
The user has been created and the crud_car_db
policy applied. The steps to test this as the new user is to login and read the credentials.
The command to login as the demo_user
is:
1
vault login -method=userpass username=demo_user password=demo_password
Successfully logging in, again will look like:
To test the credentials, the same read command will be used but for the new role:
1
vault read database/creds/cars-db-role
The user can now read the cars-db-role
credentials:
Congratulations you have successfully generated credentials that will allow all permissions on the demo_db
database with the MySQL database.
The last step after all the configuration is to confirm the credentials created work and allow the user to access the demo_db
database.
1
mysql -h database-1.csbn9npl7lxh.us-east-2.rds.amazonaws.com -P 3306 -u v-userpass-c-cars-db-ro-Ewv31tGT -p
To select the demo_db
and ‘use’ it:
1
use demo_db;
It should work and look like:
This user can now list the tables, insert a value and select everything from that table.
The mysql commands for this are:
1
2
3
4
5
show tables; # listing tables
INSERT INTO Cars (NAME) VALUES ("Holden"); # insert into the table
SELECT * FROM Cars; # select everything from the Cars table
Congratulations, if you are down this far of the tutorial, you should have 2 roles, 2 users within Vault that can create 2 different short term credentials with different permissions to the same RDS MySQL database.
To list all users that have been created use the following command:
Please note, this will need to be executed as the root user because none of the new users were given permissions to read the path.
1
vault list auth/userpass/users
TL;DR Using short-lived secrets to access a database is much more secure than standard credentials
I recently received my AWS Solutions Architect Associate Cert, and I inadvertently learned more about Ops and DevOps in respect to automation, deployment an...
This post will cover the following: Connecting to Splunk with the Python SDK, executing a search and receiving the results Connecting to Splunk without ...
TL;DR: Create Logstash conf.d file to allow Winlogbeat to be ingested into Logstash. Change Winlogbeat config file to use Logstash instead of Elasticsearch.
TL;DR Enable-PSRemoting Invoke-Command
I have been working on Windows and needed to connect to a Network Interface (NIC). I ran into problems, here is what I learned and hope it saves the same tro...
I have been using tcpdump recently and wanted to note down some of the commands Y’know, for future reference.
Today I was trouble shooting a machine at work. I did not have access via RDP or VNC, so I used SSH to forward my traffic to the host so I could access a URL.
I participated in a DevSecOps type workshop on Saturday (May 9th) in which we created some GitHub Actions. This is a post to solidify the learning and be a c...
This post is a cheat sheet for removing values from a Slice (technically creating a new slice).
On April 25th I was fortunate enough to participate in the Trend Micro Threat Defense workshop.
Since I blogged about my experience at OpenSoc, I wanted to expand on the value I found in my eLearnSecuirty Incident Response course. What you will find bel...
So Thursday (April 9th) I participated in an online blue team defense simulation event, known as OpenSOC.
I have been working with Golang strings and how to manipulate them. Working from other blogs posts I’ve found. When I sit down to code, I seem to forget ever...
its workings
You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different wa...
Blog from home installed jekyll on home PC, pulled GH repo. done :) (not that easy)
2nd blog post this is some wording