Optimizing Secure Management of Secrets, API Keys, and Credentials on Linux with HashiCorp Vault

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Context: Why is effective secrets management essential?

Working with IT systems, I’ve observed a common but risky problem: managing sensitive information, also known as secrets. These secrets can be third-party API keys, database credentials, SSL certificates, access tokens, or even root server passwords. Often, developers tend to store them in configuration files (.env), environment variables, or, worse, hardcode them directly into the source code.

My server was once subjected to a brute-force SSH attack, and I had to handle an emergency in the middle of the night. Since then, I always prioritize setting up security from the start.

Security isn’t just about hardening systems or using strong passwords; it’s also about how we manage sensitive information like API keys, database credentials, or tokens. A small vulnerability in storing or transmitting secrets can lead to very serious consequences, potentially resulting in customer data breaches or even a complete loss of control over the entire system.

Manual secrets management is not only less secure but also poses challenges when scaling or making changes. For example, if you have dozens of services sharing a single database, updating the database password becomes a nightmare. This is why I sought professional solutions, and HashiCorp Vault stood out as a prominent choice.

To generate strong and unique passwords, I often use online tools like the Password Generator on ToolCraft.app. This tool runs entirely in the browser and doesn’t send data to the server, so I feel very secure using it to create passwords for multiple services. However, strong passwords alone are not enough. More importantly, it’s about how we store and use them securely and under control.

HashiCorp Vault is an open-source tool designed to securely manage the lifecycle of secrets. It provides centralized storage, encryption capabilities, policy-based access control, and, notably, the ability to generate dynamic secrets on demand. As a result, Vault helps minimize the risk of sensitive information exposure, simplifies management, and automates security tasks.

Installing HashiCorp Vault on Linux

In this section, I will guide you through installing HashiCorp Vault on a Linux server, specifically Ubuntu Server 22.04 LTS. This process is quite straightforward and very suitable for initial deployment.

1. Download and Install Vault

First, we need to download Vault. You can visit HashiCorp Vault’s official website to get the link for the latest version, or use wget as follows:

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault

After installation, check the Vault version to confirm everything is ready:

vault --version

The displayed result will be similar to Vault v1.X.X ('X.X.X+ent' for enterprise).

2. Configure Vault Server

For Vault to function, we need to create a configuration file. I will set up a basic configuration with a filesystem storage backend, which is well-suited for development or testing environments. For production environments, you should consider using more robust backends like Consul, Raft, or PostgreSQL to ensure high availability.

Create necessary user, group, and directories

sudo useradd --system --home /etc/vault.d --shell /bin/false vault
sudo mkdir -p /etc/vault.d
sudo mkdir -p /var/lib/vault
sudo chown -R vault:vault /var/lib/vault
sudo chmod 755 /var/lib/vault

Create the configuration file /etc/vault.d/vault.hcl

Use the command sudo nano /etc/vault.d/vault.hcl then paste the following content:

storage "file" {
  path = "/var/lib/vault"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = "true" # Disable TLS for dev/test environments. TLS must be enabled for production!
}

# UI allows accessing Vault via a browser (optional)
ui = true

disable_mlock = true

Important note: tls_disable = "true" is for development environments only. In production environments, TLS MUST be enabled to encrypt all communication with Vault. I also enabled ui = true so that I can easily access Vault’s web interface later.

3. Create Systemd Service

To have Vault automatically start with the system and facilitate convenient management, I will create a Systemd service.

Create the file /etc/systemd/system/vault.service:

sudo nano /etc/systemd/system/vault.service

Next, paste the following content:

[Unit]
Description="HashiCorp Vault - A tool for managing secrets"
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target

[Service]
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep capabilities
AmbientCapabilities=CAP_IPC_LOCK
Capabilities=CAP_IPC_LOCK+ep
CapabilityBoundingSet=CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/bin/vault server -config=/etc/vault.d/vault.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60s
StartLimitBurst=3
LimitNOFILE=65536
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target

Save and exit the file. Then, you need to reload Systemd, enable, and start the Vault service:

sudo systemctl daemon-reload
sudo systemctl enable vault
sudo systemctl start vault

To check the service status, use the command:

sudo systemctl status vault

You will see Vault displaying an active (running) status.

Detailed Vault Configuration

Once the Vault server has started, the next step is to initialize and configure it to begin storing and managing secrets.

1. Initialize Vault

When Vault first starts, it will be in a sealed state. This means Vault cannot access encrypted data. To unseal Vault, we need to perform initialization. This process will generate Unseal Keys and a Root Token.

Run the following command:

vault operator init

You will receive output similar to the following:

Unseal Key 1: Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Unseal Key 2: Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
Unseal Key 3: Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
Unseal Key 4: Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Unseal Key 5: Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

Initial Root Token: hvs.Cccccccccccccccccccccccccccccccccccccccc

Vault initialized with 5 key shares and a key threshold of 3. Please
safely distribute the key shares you see above. When the Vault is re-sealed,
restarted, or stopped, you must provide at least 3 of these key shares
to unseal it again.

Extremely Important: You must carefully store these Unseal Keys and Root Token in a secure location. The Root Token has full access to Vault and should only be used once for initial configuration. Afterward, revoke it and switch to using authentication methods with lower privileges. Unseal Keys are essential to unseal Vault each time it restarts or is sealed.

2. Unseal Vault

With a key_threshold of 3, I need to provide at least 3 of the 5 Unseal Keys received to unseal Vault.

vault operator unseal
# Paste Unseal Key 1 here

vault operator unseal
# Paste Unseal Key 2 here

vault operator unseal
# Paste Unseal Key 3 here

Once enough keys are provided, Vault will transition to an unsealed state.

3. Login

To interact with Vault, we need to log in. First, set the VAULT_ADDR environment variable:

export VAULT_ADDR='http://127.0.0.1:8200'

Now, log in using the Root Token:

vault login hvs.Cccccccccccccccccccccccccccccccccccccccc

You will see the message Success! You are now authenticated.

4. Enable and Configure Secret Engines (KV Secret Engine v2)

Secret Engines are Vault components for storing, creating, or encrypting secrets. The KV (Key-Value) Secret Engine is the most common type, allowing you to store secrets as key-value pairs.

Enable KV Secret Engine v2 (version 2 supports secret versioning):

vault secrets enable -version=2 kv

Write a secret:

vault kv put kv/my-app/config username=devuser password="supersecretpassword123"

Read secret:

vault kv get kv/my-app/config

The output will display the stored secrets. When working with JSON output from Vault, sometimes the JSON string is quite long and difficult to read. I often copy that output into the JSON Formatter & Validator on ToolCraft.app to reformat it, making it easier to analyze. It’s important to remember that sensitive information (secrets) must be handled carefully. Do not paste them into online tools if you are unsure about their security. For ToolCraft specifically, since all processing occurs in the browser, I feel more confident using it.

5. Policies

Policies in Vault define the permissions a token or AppRole can access. I will create a sample policy for a development application.

Create the file dev-policy.hcl:

path "kv/data/my-app/config" {
  capabilities = ["read"]
}

path "kv/metadata/my-app/config" {
  capabilities = ["list"]
}

This policy allows reading secrets at the path kv/my-app/config and listing its metadata.

Write the policy to Vault:

vault policy write dev dev-policy.hcl

6. Authentication Methods (Auth Methods – AppRole)

AppRole is a popular authentication method for applications. It allows applications to automatically retrieve tokens from Vault without hardcoding credentials.

Enable AppRole Auth Method:

vault auth enable approle

Create a role named dev-app and assign the newly created dev policy:

vault write auth/approle/role/dev-app token_policies="dev" token_ttl="1h" token_max_ttl="24h"

Read the role_id for the dev-app role (role_id is public and can be distributed to applications):

vault read auth/approle/role/dev-app/role-id

Create a secret_id for the dev-app role (secret_id is private, must be kept absolutely secure, and only granted to the application):

vault write -f auth/approle/role/dev-app/secret-id

Your application will use this role_id and secret_id to send requests to Vault. From there, it will receive a Vault Token with permissions according to the dev policy. This token will then be used to read the necessary secrets.

Example of Python code to retrieve a secret from Vault:

import os
import hvac # pip install hvac

VAULT_ADDR = os.environ.get('VAULT_ADDR', 'http://127.0.0.1:8200')
APPROLE_ROLE_ID = "your_role_id_here"
APPROLE_SECRET_ID = "your_secret_id_here"

client = hvac.Client(url=VAULT_ADDR)

try:
    # Login using AppRole
    login_response = client.auth.approle.login(role_id=APPROLE_ROLE_ID, secret_id=APPROLE_SECRET_ID)
    client.token = login_response['auth']['client_token']

    # Read secret
    read_response = client.secrets.kv.v2.read_secret_version(path='my-app/config', mount_point='kv')
    
    username = read_response['data']['data']['username']
    password = read_response['data']['data']['password']

    print(f"Username: {username}")
    print(f"Password: {password}")

except hvac.exceptions.VaultError as e:
    print(f"Error interacting with Vault: {e}")
except Exception as e:
    print(f"Unknown error: {e}")

In practice, you would not hardcode role_id and secret_id directly into the code. Instead, provide them via environment variables or another secure mechanism during deployment.

Vault Monitoring and Auditing

After setting up Vault, monitoring and auditing are crucial steps. They ensure the system operates stably and securely.

1. Check Vault Status

You can check the overall status of Vault with the command:

vault status

This command will display information about Vault’s sealed status, high availability (if applicable), and address.

2. Audit Logging

Vault has the capability to log all requests and responses, providing you with a detailed record of who accessed what and when. This feature is indispensable for auditing purposes and enhancing security.

Enable file audit log:

vault audit enable file file_path=/var/log/vault_audit.log

Once enabled, all interactions with Vault will be logged to the file /var/log/vault_audit.log. You need to ensure this log file is protected and only authorized users have access.

3. Backup and Restore

Although I used a filesystem backend for this example, in a production environment, you MUST have a clear backup strategy for the backend storage (such as Consul, Raft, PostgreSQL, etc.). Most importantly, you must protect the Unseal Keys. Without the Unseal Keys, you will not be able to re-access the data when Vault restarts and returns to a sealed state.

4. Important Security Considerations

  • Secure Unseal Keys: Absolutely do not store all Unseal Keys in the same location. Distribute them to truly trusted individuals.
  • Use TLS: Always enable TLS for all communication with Vault in production environments. This helps protect data during transmission.
  • Principle of Least Privilege: Grant users or applications only the necessary permissions, never more than what is required.
  • Regular Auditing: Regularly review audit logs to promptly detect any unusual activities.
  • Rotate Secrets and Tokens: Leverage Vault’s dynamic secret generation capabilities and periodically rotate tokens and secrets. This significantly reduces risk.

Conclusion

HashiCorp Vault is a flexible and effective solution for securely managing secrets. Investing time in learning and deploying Vault will bring long-term benefits to your system, giving you greater peace of mind regarding security. With this basic knowledge of installation and configuration, I hope you can embark on your journey to proactively and professionally protect sensitive information.

Share: