Hashicorp Vault Keystore

This tutorial shows how to setup a KES server that uses Vault’s K/V engine as a persistent and secure key store:

K E S C l i e n t K E S S e r v e r V a u l t

Prerequisites

To start a development server or interact with Vault using the Vault CLI, download the Vault binary.

Vault Server

KES requires Vault K/V engine v1 or v2 and credentials for either the AppRole or Kubernetes authentication method.

If you do not have an existing Vault cluster available, do one of the following:

  1. Follow Hashicorp Vault install guide to create a new cluster
  2. Create a single node dev instance

The docs below discuss setting up a single node dev instance for development purposes using the AppRole authentication method.

Single Node Dev Vault Instance

The following command starts a Vault server in dev mode:

This starts a single-node Vault server in dev mode listening on 127.0.0.1:8200. A dev server is ephemeral and is not meant to be run in production.

Connect Vault to the Vault CLI

  1. Set VAULT_ADDR endpoint

    The Vault CLI needs to know the Vault endpoint:

    export VAULT_ADDR='https://127.0.0.1:8200'
    
  2. Set VAULT_TOKEN

    The Vault CLI needs an authentication token to perform operations.

    export VAULT_TOKEN=hvs.O6QNQB33ksXtMxtlRKlRZL0R
    

    Replace the token value to your own Vault access token, such as the Root token provided in the output of the vault server -dev command.

  3. Enable K/V Backend

    KES stores the secret keys at the Vault K/V backend. Vault provides two K/V engines, v1 and v2.

    MinIO recommends the K/V v1 engine.

    The Vault policy for KES depends on the chosen K/V engine version. A policy designed for K/V v1 will not work with a K/V v2 engine. Likewise, a policy designed for K/V v2 will not work with a K/V v1 engine.

    For more information about migrating from v1 to v2 refer to the Hashicorp docs on upgrading from v1.

Setup KES Access to Vault

  1. Create Vault Policy

    The Vault policy defines the API paths the KES server can access. Create a text file named kes-policy.hcl.

    The contents of the policy vary depending on the K/V engine used.

  2. Write the policy to the Vault

    The following command creates the policy at Vault:

    vault policy write kes-policy kes-policy.hcl
    
  3. Enable Authentication

    This step allows the KES server to authenticate to Vault. For this tutorial, we use the AppRole authentication method.

    vault auth enable approle
    
  4. Create KES Role

    The following command adds a new role called kes-server at Vault:

    vault write auth/approle/role/kes-server token_num_uses=0  secret_id_num_uses=0  period=5m
    
  5. Bind Policy to Role

    The following command binds the kes-server role to the kes-policy:

    vault write auth/approle/role/kes-server policies=kes-policy
    
  6. Generate AppRole ID

    Request an AppRole ID for the KES server:

    vault read auth/approle/role/kes-server/role-id 
    
  7. Generate AppRole Secret

    Request an AppRole secret for the KES server:

    vault write -f auth/approle/role/kes-server/secret-id 
    

    The AppRole secret prints as secret_id. You can ignore the secret_id_accessor.

KES Server Setup

  1. Generate KES Server Private Key & Certificate

    The following command generates a new TLS private key server.key and a self-signed X.509 certificate server.cert for the IP 127.0.0.1 and DNS name localhost (as SAN). If you want to refer to your KES server using another IP or DNS name, such as 10.1.2.3 or https://kes.example.net, adjust the --ip and/or --dns parameters accordingly.

    kes identity new --key server.key --cert server.cert --ip "127.0.0.1" --dns localhost
    

    The above command generates self-signed certificates. If you already have a way to issue certificates for your servers, you can use those.

    Other tooling for X.509 certificate generation also works. For example, you could use openssl:

    openssl ecparam -genkey -name prime256v1 | openssl ec -out server.key
    
    openssl req -new -x509 -days 30 -key server.key -out server.cert \
        -subj "/C=/ST=/L=/O=/CN=localhost" -addext "subjectAltName = IP:127.0.0.1"
    
  2. Generate an API key

    The following command generates a new KES API key.

    kes identity new
    

    The output resembles the following:

    Your API key:
    
       kes:v1:ABfa1xsnIV0lltXQC8tHXic8lte7J6hT7EoGv6+s5QCW
    
    This is the only time it is shown. Keep it secret and secure!
    
    Your Identity:
    
       cf6c535e738c1dd47a1d746366fde7f0309d1e0a8471b9f6e909833906afbbfa
    
    The identity is not a secret. It can be shared. Any peer
    needs this identity in order to verify your API key.
    
    The identity can be computed again via:
    
        kes identity of kes:v1:ABfa1xsnIV0lltXQC8tHXic8lte7J6hT7EoGv6+s5QCW   
    

    The generated identity is NOT a secret and can be shared publicly. It will be used later on in the KES config file as admin identity or assigned to a policy.

    The API key itself is a secret and should not be shared. You can always recompute an API key’s identity.

  3. Configure KES Server

    Create the KES server configuration file: config.yml.

    Make sure that the identity in the policy section matches the client.crt identity. Add the approle role_id and secret_id obtained earlier.

    admin:
      # Use the identity generated above by 'kes identity new'.
      identity: "" # For example: cf6c535e738c1dd47a1d746366fde7f0309d1e0a8471b9f6e909833906afbbfa
    
    tls:
      key: private.key    # The KES server TLS private key
      cert: public.crt    # The KES server TLS certificate
    
    keystore:
       vault:
         endpoint: https://127.0.0.1:8200
         version:  v1 # The K/V engine version - either "v1" or "v2".
         engine:   kv # The engine path of the K/V engine. The default is "kv".
         approle:
           id:     "" # Your AppRole ID
           secret: "" # Your AppRole Secret
    
  4. Start KES Server

KES CLI Access

  1. Set KES_SERVER endpoint

    The following environment variable specifies the KES server the KES CLI should talk to:

    export KES_SERVER=https://127.0.0.1:7373
    
  2. Define the CLI access credentials

    The following environment variable sets the key the client uses to talk to a KES server:

    export KES_API_KEY=kes:v1:ABfa1xsnIV0lltXQC8tHXic8lte7J6hT7EoGv6+s5QCW
    

    Replace the value with your server’s API Key. The server’s API key displays in the output when you start the server.

  3. Test the Configuration

    For example, check the status of the server:

    kes status -k
    

    Use the key to generate a new data encryption key:

    kes key dek my-key-1 -k
    

    The command output resembles the following:

    {
      plaintext : UGgcVBgyQYwxKzve7UJNV5x8aTiPJFoR+s828reNjh0=
      ciphertext: eyJhZWFkIjoiQUVTLTI1Ni1HQ00tSE1BQy1TSEEtMjU2IiwiaWQiOiIxMTc1ZjJjNDMyMjNjNjNmNjY1MDk5ZDExNmU3Yzc4NCIsIml2IjoiVHBtbHpWTDh5a2t4VVREV1RSTU5Tdz09Iiwibm9uY2UiOiJkeGl0R3A3bFB6S21rTE5HIiwiYnl0ZXMiOiJaaWdobEZrTUFuVVBWSG0wZDhSYUNBY3pnRWRsQzJqWFhCK1YxaWl2MXdnYjhBRytuTWx0Y3BGK0RtV1VoNkZaIn0=
    }
    

Using KES with a MinIO Server

MinIO Server requires KES to enable server-side data encryption.

See the KES for MinIO instruction guide for additional steps needed to use your new KES Server with a MinIO Server.

Configuration References

The following section describes the Key Encryption Service (KES) configuration settings to use Hashicorp Vault Keystore as the root KMS to store external keys, such as the keys used for Server-Side Encryption on a MinIO Server.

MinIO Server Requires Expanded Permissions:
Starting with MinIO Server RELEASE.2023-02-17T17-52-43Z, MinIO requires expanded KES permissions for functionality. The example configuration in this section contains all required permissions.

Advanced Configuration

These additional configuration steps may solve specific problems.

Multi-Tenancy with K/V prefixes

Vault can serve as backend for multiple, isolated KES tenants. Each KES tenant can consist of N replicas. There can be M KES tenants connected to the same Vault server/cluster.

This means N × M KES server instances can connect to a single Vault.

In these configurations, each KES tenant has a separate prefix at the K/V secret engine. For each KES tenant, there must be a corresponding Vault policy.

  • For K/V v1:

    path "kv/<tenant-name>/*" {
       capabilities = [ "create", "read", "delete" ]
    }
    
  • For K/V v2:

    path "kv/data/<tenant-name>/*" {
      capabilities = [ "create", "read" ]
    }
    path "kv/metadata/<tenant-name>/*" {
      capabilities = [ "list", "delete" ]       
    }
    

Create a different configuration file for each KES tenant. The file contains the Vault K/V prefix for the tenant to use.

keystore:
   vault:
     endpoint: https://127.0.0.1:8200
     prefix: <tenant-name>
     approle:
       id:     "" # Your AppRole ID
       secret: "" # Your AppRole Secret
       retry:  15s
     status:
       ping: 10s
     tls:
       ca: vault.crt # Manually trust the vault certificate since we use self-signed certificates

Multi-Tenancy with Vault Namespaces

Vault can serve as the backend for multiple, isolated KES tenants. Each KES tenant can consist of N replicas. There can be M KES tenants connected to the same Vault server/cluster.

This means N × M KES server instances can connect to a single Vault.

Therefore, each KES tenant has a separate prefix at the K/V secret engine. For each KES tenant there has to be a corresponding Vault policy.

  • For K/V v1:

    path "kv/<tenant-name>/*" {
       capabilities = [ "create", "read", "delete" ]
    }
    
  • For K/V v2:

    path "kv/data/<tenant-name>/*" {
       capabilities = [ "create", "read" ]
    }
    path "kv/metadata/<tenant-name>/*" {
       capabilities = [ "list", "delete" ]       
    }
    

Use a different configuration file for each KES tenant. The file contains the Vault namespace which the KES tenant should use.

keystore:
   vault:
     endpoint: https://127.0.0.1:8200
     namespace: <vault-namespace>
     approle:
       id:     "" # Your AppRole ID
       secret: "" # Your AppRole Secret
       retry:  15s
     status:
       ping: 10s
     tls:
       ca: vault.crt # Manually trust the vault certificate since we use self-signed certificates

Encrypt Vault-stored Keys

Hashicorp’s Transit functionality provides a means to encrypt and decrypt keys stored in the vault. This provides an additional layer of encryption that may be useful in specific cases.

When enabled, Hashicorp stores a key in the Vault to encrypt or decrypt the other keys stored in the vault. KES then uses the vault-managed key to store or retrieve keys from the Vault.

Potential data loss:
If the specified transit key is incorrect, disabled, removed, or otherwise unaccessible, KES cannot retrieve any vault keys nor perform any en/decryption operations relying on those keys.

To configure Transit, add the following section to the KES Configuration YAML’s keystore.vault section:

keystore:
  vault:
    transit:      # Optionally encrypt keys stored on the K/V engine with a Vault-managed key.
      engine: ""  # The path of the transit engine - e.g. "my-transit". If empty, defaults to: transit (Vault default)
      key: ""     # The key name that should be used to encrypt entries stored on the K/V engine.

References