Skip to main content

HashiCorp Vault: Docker Compose Deployment with TLS Encryption and Web-UI, Initial Setup via Vault CLI, Key/Value Secret Example

1136 words·
HashiCorp Vault Docker-Compose
Table of Contents
HashiCorp-Vault - This article is part of a series.
Part 1: This Article

Overview
#

I’m using an Ubuntu 24.04 LTS with the IP 192.168.30.19 in this tutorial.

Prerequisites
#

Docker User
#

# Create a system user for Docker
sudo adduser --system --ingroup docker --shell /sbin/nologin docker-system

DNS Entry
#

Make sure both the client that accesses Vault webinterface and the Vault host itself are able to resolv the Vault DNS domain name.

# Create a DNS entry for Vault
192.168.30.19 vault.jklug.work

Vault Setup
#

Overview
#

The Vault Docker Compose file and folder structure should look like this:

vault
├── docker-compose.yaml
└── vault
    ├── certs
    │   ├── fullchain.pem
    │   └── privkey.pem
    ├── config
    │   └── vault.json
    ├── data
    └── policies

Folder Structure
#

# Create folder struture
sudo mkdir -p /opt/vault/vault/{config,policies,file,certs} && cd /opt/vault

TLS Certs
#

I’m using a Let’s Encrypt wildcard vertificate in this tutorial.

# Copy the TLS certificate
sudo cp fullchain.pem privkey.pem /opt/vault/vault/certs/

vault.json
#

# Create Vault configuration file
sudo vi vault/config/vault.json
{
  "ui": true,
  "listener": {
    "tcp": {
      "address": "0.0.0.0:8200",
      "tls_disable": "0",
      "tls_cert_file": "/vault/certs/fullchain.pem",
      "tls_key_file": "/vault/certs/privkey.pem"
    }
  },
  "backend": {
    "file": {
      "path": "/vault/file"
    }
  },
  "default_lease_ttl": "168h",
  "max_lease_ttl": "0h",
  "api_addr": "http://0.0.0.0:8200"
}

Docker Compose
#

# Create Docker Compose manifest
sudo vi docker-compose.yaml
services:

    vault:
      image: vault:1.13.3
      volumes:
        - ./vault/config:/vault/config
        - ./vault/policies:/vault/policies
        - ./vault/file:/vault/file:rw
        - ./vault/certs:/vault/certs
      ports:
        - 8200:8200
      environment:
        - VAULT_ADDR=http://0.0.0.0:8200
        - VAULT_API_ADDR=http://0.0.0.0:8200
        - VAULT_ADDRESS=http://0.0.0.0:8200
      cap_add:
        - IPC_LOCK
      command: vault server -config=/vault/config/vault.json

Change Ownership
#

# Change ownership
sudo chown -R docker-system:docker /opt/vault

Start Container
#

# Create & start containers
sudo -u docker-system docker compose up -d

Initial Setup
#

Install Vault Binary (for CLI)
#

Install the Vault binary for the Vault CLI usage:

# Install Vault
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
# Verify installation / check version
vault --version

Set Vault Address
#

Temporary:

# Export the VAULT_ADDR environment variable
export VAULT_ADDR='https://vault.jklug.work:8200'

Permanent:

# Export the VAULT_ADDR environment variable
echo 'export VAULT_ADDR=https://vault.jklug.work:8200' >> ~/.bashrc

# Apply changes
source ~/.bashrc

Initialize the Vault
#

This generates the encryption & unseal keys and sets up the initial root token.

# Initialize Vault
vault operator init

# Shell output:
Unseal Key 1: LR3YmxuhX6W/bZMD6q8XAt1IK0rnlfbkMY9ayxEQBN11
Unseal Key 2: wIAZsEMJ4VBp8Gi1ogBDgXr0/sxyflzKGEfgq0Or4ZQM
Unseal Key 3: rP4mr7hLhCAuEdFjWgyHUI6mIWTazDYAEF27J4GOBp4Z
Unseal Key 4: WVNKlxOyQvL4QAjcCoU6i0Y355iIh666wO5kqZaOjgvw
Unseal Key 5: N/Aq3JJSvMst/E1KQEieDqtOQ7fSspOv1ihN2FXihCop

Initial Root Token: hvs.kG9fJG4s56A6vIxDTPpm0i3l

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated root key. Without at least 3 keys to
reconstruct the root key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.

Unseal the Vault
#

After initialization, Vault is in a sealed state. Vault can access the physical storage, but it can’t decrypt it.

Note: To prevent the ‘unseal’ command from appearing in the Bash history, add a space character before it.

# Input three of the five unseal keys
vault operator unseal LR3YmxuhX6W/bZMD6q8XAt1IK0rnlfbkMY9ayxEQBN11
vault operator unseal wIAZsEMJ4VBp8Gi1ogBDgXr0/sxyflzKGEfgq0Or4ZQM
vault operator unseal rP4mr7hLhCAuEdFjWgyHUI6mIWTazDYAEF27J4GOBp4Z

After the third unseal keys was entered, the sealed status changes to false:

# Shell output:
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    1/3
Unseal Nonce       209d1ff0-0275-52eb-a7f7-7d46673d57a7
Version            1.13.3
Build Date         2023-06-06T18:12:37Z
Storage Type       file
HA Enabled         false

# Shell output:
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    2/3
Unseal Nonce       209d1ff0-0275-52eb-a7f7-7d46673d57a7
Version            1.13.3
Build Date         2023-06-06T18:12:37Z
Storage Type       file
HA Enabled         false

# Shell output:
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false # Sealed status changes to false
Total Shares    5
Threshold       3
Version         1.13.3
Build Date      2023-06-06T18:12:37Z
Storage Type    file
Cluster Name    vault-cluster-5225547d
Cluster ID      86d5df21-295b-5aa2-3e1d-cbb4e76f12b3
HA Enabled      false

Verify Vault Status
#

# Optional: Verify the sealed status again
vault status

# Shell output:
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.13.3
Build Date      2023-06-06T18:12:37Z
Storage Type    file
Cluster Name    vault-cluster-5225547d
Cluster ID      86d5df21-295b-5aa2-3e1d-cbb4e76f12b3
HA Enabled      false

Login / Authenticate
#

Once Vault is unsealed, use the initial root token to authenticate:

# Login
vault login hvs.kG9fJG4s56A6vIxDTPpm0i3l

# Shell output:
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                hvs.kG9fJG4s56A6vIxDTPpm0i3l
token_accessor       dONSpNlr4AiawUyOS1ioITGT
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

Reseal the Vault
#

# Reseal the Vault
vault operator seal

Create Secret
#

Enable Secrets Engine
#

Vault can manage various types of secrets through what it calls “secrets engines.”

# Enable a Key/Value (KV) secrets engine
vault secrets enable -path=project-1 kv-v2

# Shell output:
Success! Enabled the kv-v2 secrets engine at: project-1/
# Verify the secrets engine
vault secrets list

# Shell output:
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_92bb725d    per-token private secret storage
identity/     identity     identity_002dc7ca     identity store
project-1/    kv           kv_91e9cd44           n/a
sys/          system       system_4aa0af3b       system endpoints used for control, policy and debugging
# Disable the Key/Value (KV) secrets engine
vault secrets disable project-1

Store a Secret
#

# Store a secret
vault kv put project-1/development/admin username="admin" password="my-secure-pw"

# Shell output:
======= Metadata =======
Key                Value
---                -----
created_time       2024-08-01T15:44:18.223265885Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

Update the Secret
#

# Update the secret username
vault kv patch project-1/development/admin username="newadmin"

# Shell output:
========== Secret Path ==========
project-1/data/development/admin

======= Metadata =======
Key                Value
---                -----
created_time       2024-08-01T15:45:47.947081488Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

Read the Secret
#

# List the secret
vault kv get project-1/development/admin

# Shell output:
========== Secret Path ==========
project-1/data/development/admin

======= Metadata =======
Key                Value
---                -----
created_time       2024-08-01T15:45:47.947081488Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

====== Data ======
Key         Value
---         -----
password    my-secure-pw
username    newadmin

Delete the Secret
#

# Delete the secret: Latest version
vault kv delete project-1/development/admin

# Delete the secret: Specific version (1 & 2)
vault kv delete -versions=1,2 project-1/development/admin

# Delete the secret: All versions (1 & 2)
vault kv metadata delete project-1/development/admin

# Shell output:
Success! Data deleted (if it existed) at: project-1/data/development/admin

Access Vault GUI
#

Login
#

# Access the Vault webinterface
https://vault.jklug.work:8200/

Once Vault is unsealed, use the initial root token to authenticate:


Verify the Secret
#

Verify the previously created secret:

  • Go to: “Secrets”

  • Select the project-1 secrets engine

  • Select “development/admin”

  • Show the admin password


Links #

# Vault DockerHub
https://hub.docker.com/_/vault
HashiCorp-Vault - This article is part of a series.
Part 1: This Article