What is Vault ?

HashiCorp Vault is a popular open-source tool for securely managing secrets, such as passwords, tokens, and certificates, in modern cloud and container environments.

Vault provides a centralized way to store and manage secrets, which can be accessed programmatically through a variety of APIs or via a web interface. It also provides access control mechanisms to ensure that only authorized users and applications can access specific secrets.

Vault uses a variety of encryption techniques to ensure that secrets are stored securely, including encryption at rest and in transit. It also supports various authentication methods, such as LDAP, Kerberos, and OAuth, to authenticate users and applications.

Some common use cases for Vault include managing database credentials, API keys, SSH keys, and TLS certificates. By using Vault, organizations can improve their security posture by ensuring that secrets are stored securely and accessed only by authorized users and applications.

  1. Centralized Secret Management: Vault provides a centralized system for storing and managing secrets such as API keys, passwords, database credentials, certificates, and more. It allows you to store these secrets securely in one place rather than scattering them across different applications and systems.
  2. Secure Secret Storage: Vault uses encryption to securely store secrets, ensuring that sensitive information is protected at rest. It leverages encryption algorithms and key management techniques to safeguard secrets from unauthorized access.
  3. Dynamic Secrets: One of the key features of Vault is the ability to generate dynamic secrets. Instead of manually provisioning and rotating credentials, Vault can dynamically create and manage them on-demand. This improves security by reducing the exposure of long-lived credentials and automates the process of credential management.
  4. Fine-Grained Access Control: Vault offers robust access control mechanisms, allowing you to define granular policies for accessing secrets. You can restrict access based on various factors such as IP addresses, time of day, user identity, and more. This enables you to implement the principle of least privilege and ensure that only authorized entities can access specific secrets.
  5. Secret Revocation and Expiration: Vault allows you to revoke and expire secrets at any time. If a secret is compromised or no longer needed, you can instantly revoke its access, rendering it unusable. This provides an additional layer of security and helps mitigate the impact of a potential security breach.
  6. Secrets Encryption as a Service: Vault offers a Secrets Encryption as a Service (SEaaS) feature, which allows you to encrypt and decrypt data using encryption keys managed by Vault. This simplifies the process of data encryption and ensures consistent encryption practices across applications and environments.
  7. Audit and Compliance: Vault provides detailed audit logs that track all access and operations performed on secrets. These logs can be used for compliance purposes, forensic analysis, and troubleshooting. Vault integrates with external logging and monitoring systems, making it easier to centralize and analyze security-related events.
  8. High Availability and Scalability: Vault is designed to be highly available and scalable, ensuring that secrets remain accessible even in the event of hardware or network failures. It supports various deployment architectures, including replication and clustering, to meet the demands of enterprise-scale environments.
  9. Integration and Ecosystem: Vault has a rich ecosystem and integrates with popular infrastructure and application tools. It provides libraries and APIs for seamless integration with applications, automation frameworks, configuration management tools, cloud platforms, and more. This allows you to leverage Vault’s capabilities across your entire technology stack.
  10. Community Support and Active Development: HashiCorp Vault has a vibrant community of users and contributors. The tool is actively developed and maintained, with regular updates and new features being released. This ensures ongoing support, bug fixes, and the addition of new security enhancements.

Installation with Docker

Dev mode

Take care, data are stored only in :memory: when vault is run in dev mode !

docker-compose.yaml
version: "3.3"
services:
vault:
command: vault server -dev
image: hashicorp/vault:latest # TODO ADD TAG
ports:
- 8200:8200
environment:
- VAULT_DEV_ROOT_TOKEN_ID=alex
- VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200
- VAULT_ADDR=http://0.0.0.0:8200

Production mode

Here, the data are stored in a volume !

docker-compose.yaml
version: "3.3"
volumes:
volume_vault:
services:
vault:
command: vault server -config=/tmp/config.hcl
image: hashicorp/vault:1.13.2
ports:
- 8200:8200
cap_add:
- IPC_LOCK
environment:
- VAULT_ADDR=http://0.0.0.0:8200
volumes:
- ./vault.hcl:/tmp/config.hcl:ro
- volume_vault:/vault/
healthcheck:
test: ["CMD-SHELL", 'vault status | grep "Seal.*false"']
interval: 5s
timeout: 5s
retries: 20

vault.hcl
ui = true
storage "file" {
path = "/vault/data"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}
disable_mlock = true

Functionnalities of Hashicorps Vault

Secrets engines

Vault provides various secrets engines, which are plugins that enable Vault to interact with different types of secrets and securely store them. Some of the most commonly used secrets engines include:

  1. KV (Key-Value) Engine : The KV engine is a fundamental secrets engine that allows you to store arbitrary key-value secrets. It’s a simple yet versatile engine, making it suitable for various use cases.

  2. PKI (Public Key Infrastructure) Engine : The PKI engine is used to manage X.509 certificates and private keys, making it ideal for implementing a robust Public Key Infrastructure within your organization.

  3. SSH Engine : With the SSH engine, Vault can dynamically generate SSH key pairs and sign SSH keys for secure access to servers, improving SSH key management and enhancing security.

  4. TOTP (Time-Based One-Time Password) Engine : The TOTP engine generates time-based one-time passwords, often used for two-factor authentication (2FA) in applications.

  5. Kubernetes Engine : The Kubernetes engine allows Vault to issue short-lived client certificates to Kubernetes pods, enhancing security within a Kubernetes environment.

And many more! The availability of multiple secrets engines gives you the flexibility to securely store different types of secrets according to your specific requirements.

Access

Managing authentication methods and controlling access to Vault is essential for maintaining a secure environment. Vault offers several authentication methods to verify users’ identities:

  1. AppRole : AppRole is designed for applications and services to authenticate with Vault to access secrets. It provides a way for applications to authenticate without exposing sensitive credentials.

  2. Tokens : Tokens are a core authentication method in Vault. They are issued to users upon successful login and act as temporary access credentials to interact with the Vault.

  3. Username & Password : Vault supports traditional username and password-based authentication for users who need direct access to Vault.

  4. JWT (JSON Web Tokens) : With JWT authentication, users can authenticate using JSON Web Tokens, which are commonly used in web applications and APIs.

By configuring the appropriate authentication methods, you can ensure that only authorized users and applications can access sensitive data stored in Vault.

Policies

Vault policies define the access control rules and permissions for different paths within Vault. These policies are written in HashiCorp Configuration Language (HCL) and allow fine-grained control over what actions users and applications can perform. Here’s an example of a Vault policy:

# List, create, update, and delete key/value secrets
path "secret/app-1"
{
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/app-2"
{
capabilities = ["read", "list"]
}

In the above example, the policy grants full CRUD (Create, Read, Update, Delete) access to the path secret/app-1, while allowing only read and list access to secret/app-2. By crafting well-defined policies, you can ensure that users and applications have the appropriate level of access to secrets within Vault.

Usage

You can connect on port 8200 with your browser.

  • If you are in dev mode, use the VAULT_DEV_ROOT_TOKEN_ID to sign in.
  • If you are in production mode, look at the logs in the vault container.

You have multiple ways to interact with Vault, Vault UI, Vault CLI and Vault REST API.

Vault CLI

Let’s use Vault CLI:

Terminal window
# PUT OUR TOKEN TO BEGIN TO INTERACT
export VAULT_TOKEN="TOKEN" # Or add flag -mount in your next commands
# SET SECRETS
vault kv put secret/petclinic MYSQL_USER=petclinicUSER MYSQL_PASSWORD=petclinicPASSWORD
# GET SECRETS
vault kv get -field=MYSQL_USER secret/petclinic
vault kv get -field=MYSQL_PASSWORD secret/petclinic
# DELETE SECRETS
vault kv delete secret/test

You can define new authentication methods:

Terminal window
vault auth enable approle

You can define policies. It is specific permissions on secrets (["create", "read", "update", "patch", "delete", "list"])

Terminal window
echo 'path "secret/data/petclinic" {
capabilities = ["read", "list"]
}' > policy.hcl
vault policy write petclinic-policy ./policy.hcl
vault token create -policy petclinic-policy

Take care, don’t forget to add /data.

You can find information about policy here : https://developer.hashicorp.com/vault/tutorials/policies/policies. You can create an admin policy to avoid using root token.

REST API

You can use REST API to manipulate secrets. This would be a request:

Terminal window
GET http://localhost:8200/v1/secret/data/petclinic HTTP/1.1
X-Vault-Token: <TOKEN>
# -----
curl -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_URL

Example : Manage an AppRole with REST API

Terminal window
# First, we define our Token here
export VAULT_TOKEN="alex"
# ENABLE APP ROLE ; only the first time :)
curl \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request POST \
--data '{"type": "approle"}' \
http://localhost:8200/v1/sys/auth/approle
# Then, we need to create our application
curl \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request POST \
--data '{
"policies": "admin",
"token_num_uses": 10,
"token_ttl": "20m",
"token_max_ttl": "30m",
"secret_id_ttl": 0,
"secret_id_num_uses": 0
}' \
http://localhost:8200/v1/auth/approle/role/ansible-app
# We fetch the "login" of the application
curl \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
http://localhost:8200/v1/auth/approle/role/ansible-app/role-id
# ROLE_ID
# 76ec19c8-7998-d306-ab0c-50ef2adf7309
# And we need to fetch the "password" of the application
# Note : we can have multiple passwords for one application
# We can say like : "We can use a password only 10 times", or : "This password is valid only for 20 minutes".
curl \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request POST \
http://localhost:8200/v1/auth/approle/role/ansible-app/secret-id
# SECRET_ID
# 190ca1d5-9200-d18e-a2dd-1615fc97ea31
# Then, we log in with the 2 parameters
curl \
--request POST \
--data '{"role_id":"76ec19c8-7998-d306-ab0c-50ef2adf7309","secret_id":"190ca1d5-9200-d18e-a2dd-1615fc97ea31"}' \
http://localhost:8200/v1/auth/approle/login
Terminal window
# Then, we can fetch a secret with this command :
curl -H "X-Vault-Token: hvs.CAESIAT7aV3eV3jAKUxQy7vQb1rF9RpAPUUmMPYUlY5ZxD5LGh4KHGh2cy5pUGt1ZjZ6WlFkR1lXZ1R5cWxPeVZZM1I" http://localhost:8200/v1/secret/data/servers

Full vault example with docker, setup and usage

In this example, we want to use Vault with this project : https://github.com/spring-projects/spring-petclinic

docker-compose.yaml
version: "3.7"
volumes:
volume_mysql:
volume_vault:
services:
petclinic:
image: petclinic
build:
context: .
dockerfile: ./Dockerfile_petclinic
ports:
- 8080:8080
environment:
- MODE=VAULT
- VAULT_TOKEN=<TOKEN_WITH_POLICIES>
- VAULT_URL=http://vault:8200/v1/secret/data/petclinic
# restart: always
healthcheck:
test:
["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"]
interval: 5s
timeout: 5s
retries: 20
depends_on:
mysql:
condition: service_healthy
vault:
condition: service_healthy
mysql:
image: mysql:8.0
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=passwordRoot
- MYSQL_ALLOW_EMPTY_PASSWORD=true
- MYSQL_USER=petclinicUSER
- MYSQL_PASSWORD=petclinicPASSWORD
- MYSQL_DATABASE=petclinic
healthcheck:
test:
[
"CMD-SHELL",
"mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD",
]
interval: 5s
timeout: 5s
retries: 20
# volumes:
# - ./volume_mysql:/var/lib/mysql
vault:
command: vault server -config=/tmp/config.hcl
image: hashicorp/vault:1.13.2
ports:
- 8200:8200
cap_add:
- IPC_LOCK
environment:
- VAULT_ADDR=http://0.0.0.0:8200
volumes:
- ./vault.hcl:/tmp/config.hcl:ro
- volume_vault:/vault/
healthcheck:
test: ["CMD-SHELL", 'vault status | grep "Seal.*false"']
interval: 5s
timeout: 5s
retries: 20

The petclinic token can be get with these commands for example:

Terminal window
export VAULT_TOKEN="ROOT_TOKEN"
vault kv put secret/petclinic MYSQL_USER=petclinicUSER MYSQL_PASS=petclinicPASSWORD MYSQL_URL=jdbc:mysql://mysql/petclinic DB_TYPE=mysql
vault kv get -field=MYSQL_USER secret/petclinic
vault kv get -field=MYSQL_PASS secret/petclinic
# POLICY
echo 'path "secret/data/petclinic" {
capabilities = ["read", "list"]
}' > policy.hcl
vault policy write petclinic-policy ./policy.hcl
vault token create -policy petclinic-policy

Or create an approle

Terminal window
# https://developer.hashicorp.com/vault/tutorials/auth-methods/approle#prerequisites
vault auth enable approle
vault policy write app-1234 -<<EOF
# Read-only permission on secrets stored at 'secret/data/mysql/webapp'
path "secret/data/app-1234" {
capabilities = [ "read", "list" ]
}
EOF
vault write auth/approle/role/app-1234 token_policies="app-1234" \
token_ttl=1h token_max_ttl=4h
vault read auth/approle/role/app-1234
vault read auth/approle/role/app-1234/role-id # Get role_id
vault write -force auth/approle/role/app-1234/secret-id # Get secret_id
vault write auth/approle/login role_id="0aeaff2d-9882-520a-3d89-afcce4c80bcb" \
secret_id="b4e8b0b9-7e1c-3f28-d351-db48f01e10fb"
export APP_TOKEN="hvs.CAESIBH-bjqyfQnnayKwWVJ6WfiTfU1Cari7_gUgAsdd70JlGh4KHGh2cy4wZUE5ZDdIb2xaeXdtN0RkVFFYdkVYbkQ"
VAULT_TOKEN=$APP_TOKEN vault kv get secret/app-1234

The last command would generate a new token with correct policies.

The Dockerfile of petclinic application is :

Dockerfile
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app/
COPY ./petclinic ./
RUN ./mvnw package
FROM eclipse-temurin:17-jre-alpine AS prod
WORKDIR /opt/spring-petclinic/
RUN apk add jq curl bash
COPY --from=build /app/target/spring-petclinic-3.0.0-SNAPSHOT.jar ./lib/application.jar
COPY ./entrypoint_petclinic.bash ./bin/entrypoint.bash
COPY --from=build /app/src/main/resources/application-template.properties ./etc/
RUN chmod +x ./bin/entrypoint.bash
ENTRYPOINT [ "./bin/entrypoint.bash" ]

In the entrypoint.bash, we want to get the Vault secrets to setup the MySQL database connection. We need to create a file named application-mysql.properties, and fill this file. So we can create a application-template.properties file first containing:

application-template.properties
# Database
# database init, supports mysql too
database=mysql
spring.datasource.url={{MYSQL_URL}}
spring.datasource.username={{MYSQL_USER}}
spring.datasource.password={{MYSQL_PASS}}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always

Note : the variables we want to replace are inside {{ }}

#!/bin/bash
case "$MODE" in
CREDENTIALS)
echo "TODO"
exit 1
;;
VAULT)
echo "VAULT ENABLE"
RESPONSE=$(curl -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_URL)
SECRET=$(echo $RESPONSE | jq -r '.data.data')
# export MYSQL_URL=$(echo "$RESPONSE" | grep -oP '(?<="MYSQL_URL":")[^"]*')
# export MYSQL_USER=$(echo "$RESPONSE" | grep -oP '(?<="MYSQL_USER":")[^"]*')
# export MYSQL_PASS=$(echo "$RESPONSE" | grep -oP '(?<="MYSQL_PASS":")[^"]*')
# export MYSQL_URL=$(echo "$RESPONSE" | jq -r '.data.data.MYSQL_URL')
# export MYSQL_USER=$(echo "$RESPONSE" | jq -r '.data.data.MYSQL_USER')
# export MYSQL_PASS=$(echo "$RESPONSE" | jq -r '.data.data.MYSQL_PASS')
DB_TYPE=$(echo "$RESPONSE" | jq -r '.data.data.DB_TYPE')
TEMPLATE_PROPERTIES_FILE='./etc/application-template.properties'
PROPERTIES_FILE="./etc/application-$(echo "$DB_TYPE").properties"
cp $TEMPLATE_PROPERTIES_FILE $PROPERTIES_FILE
grep '{{.*}}' "$PROPERTIES_FILE" |
while IFS= read -r line; do
placeholder=$(echo "$line" | awk -F= '{print $2}' | tr -d '{}')
value=$(echo "$SECRET" | jq -r ".\"$placeholder\"")
sed -i "s|{{${placeholder}}}|${value}|g" "$PROPERTIES_FILE"
done
;;
esac
java -jar ./lib/application.jar --spring.profiles.active=$DB_TYPE --spring.config.location=./etc/

Here, all the VAULT secret variables are replaced.

🎉 TADA, the application is working ! 🎉


Recommended articles