Skip to content

Last updated: 2026-06-25

HashiCorp Vault TLS/SSL Configuration Guide

This guide provides recommended TLS/SSL settings for HashiCorp Vault, a secrets management tool. Vault stores and controls access to sensitive data such as API keys, passwords, and certificates. TLS is critical for Vault because all secrets transit the network through its API.

Prerequisites

  • HashiCorp Vault 1.19 (LTS) or later
  • A valid SSL/TLS certificate
  • Vault is built with Go's crypto/tls, which provides strong TLS defaults

Certificate Setup

Vault needs a certificate that includes all hostnames and IP addresses clients will use to connect:

chmod 640 /etc/vault/ssl/privkey.pem
chown vault:vault /etc/vault/ssl/privkey.pem
chmod 644 /etc/vault/ssl/fullchain.pem

Listener Configuration

Vault's TLS settings are configured in the listener stanza of the Vault configuration file (/etc/vault.d/vault.hcl).

Basic TLS Setup

listener "tcp" {
    address     = "[::]:8200"
    tls_cert_file = "/etc/vault/ssl/fullchain.pem"
    tls_key_file  = "/etc/vault/ssl/privkey.pem"
}

Setting tls_disable = true disables TLS entirely. Never do this in production. Vault will refuse to start in production mode without TLS unless explicitly disabled, and all secrets would transit the network in plaintext.

Minimum TLS Version

listener "tcp" {
    address         = "[::]:8200"
    tls_cert_file   = "/etc/vault/ssl/fullchain.pem"
    tls_key_file    = "/etc/vault/ssl/privkey.pem"
    tls_min_version = "tls12"
}

Available values: tls10, tls11, tls12, tls13.

Cipher Suites

Vault uses Go's cipher suite names. Configure allowed cipher suites for TLS 1.2 (TLS 1.3 ciphers are not configurable in Go and are always secure):

listener "tcp" {
    address         = "[::]:8200"
    tls_cert_file   = "/etc/vault/ssl/fullchain.pem"
    tls_key_file    = "/etc/vault/ssl/privkey.pem"
    tls_min_version = "tls12"

    tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
}

Client Certificate Authentication

To require clients to present a TLS certificate (mutual TLS):

listener "tcp" {
    address                  = "[::]:8200"
    tls_cert_file            = "/etc/vault/ssl/fullchain.pem"
    tls_key_file             = "/etc/vault/ssl/privkey.pem"
    tls_min_version          = "tls12"
    tls_client_ca_file       = "/etc/vault/ssl/ca.pem"
    tls_require_and_verify_client_cert = true
}

See RFC 8446 ยง4.3.2 for the TLS Certificate Request specification, and Wikipedia: Mutual authentication for a general overview.

Disable Client Cache Headers

For additional security, disable client-side caching of Vault responses:

listener "tcp" {
    address                            = "[::]:8200"
    tls_cert_file                      = "/etc/vault/ssl/fullchain.pem"
    tls_key_file                       = "/etc/vault/ssl/privkey.pem"
    tls_min_version                    = "tls12"
    tls_disable_client_certs           = false
    custom_response_headers {
        "default" = {
            "Strict-Transport-Security" = ["max-age=63072000; includeSubDomains; preload"]
        }
    }
}

Cluster TLS

In high-availability (HA) deployments, Vault nodes communicate with each other through the cluster port. Configure TLS for cluster communication:

listener "tcp" {
    address         = "[::]:8200"
    cluster_address = "[::]:8201"
    tls_cert_file   = "/etc/vault/ssl/fullchain.pem"
    tls_key_file    = "/etc/vault/ssl/privkey.pem"
    tls_min_version = "tls12"
}

Vault automatically uses TLS for cluster communication when the listener has TLS enabled. For separate cluster certificates:

cluster_addr = "https://vault-node1.example.com:8201"

Complete Configuration

storage "raft" {
    path = "/opt/vault/data"
    node_id = "vault-node1"
}

listener "tcp" {
    address         = "[::]:8200"
    cluster_address = "[::]:8201"
    tls_cert_file   = "/etc/vault/ssl/fullchain.pem"
    tls_key_file    = "/etc/vault/ssl/privkey.pem"
    tls_min_version = "tls12"

    tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"

    custom_response_headers {
        "default" = {
            "Strict-Transport-Security" = ["max-age=63072000; includeSubDomains; preload"]
        }
    }
}

api_addr     = "https://vault.example.com:8200"
cluster_addr = "https://vault-node1.example.com:8201"
ui           = true

Vault as a CA (PKI Secrets Engine)

Vault can also act as a certificate authority using the PKI secrets engine. This is useful for issuing TLS certificates to other services:

vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki

vault write pki/root/generate/internal \
    common_name="Internal CA" \
    ttl=87600h

vault write pki/config/urls \
    issuing_certificates="https://vault.example.com:8200/v1/pki/ca" \
    crl_distribution_points="https://vault.example.com:8200/v1/pki/crl"

Client Configuration

Environment Variables

export VAULT_ADDR="https://vault.example.com:8200"
export VAULT_CACERT="/path/to/ca.pem"

For mutual TLS:

export VAULT_CLIENT_CERT="/path/to/client-cert.pem"
export VAULT_CLIENT_KEY="/path/to/client-key.pem"

CLI Usage

vault status -ca-cert=/path/to/ca.pem -address=https://vault.example.com:8200

Skip Verification (Development Only)

export VAULT_SKIP_VERIFY=true

Never use VAULT_SKIP_VERIFY in production. This disables all certificate verification.

Security Notes

Vault uses Go's crypto/tls package, which has a different vulnerability history than OpenSSL:

  • POODLE (CVE-2014-3566, 2014): SSL 3.0 has never been supported in Go's TLS implementation.
  • BEAST (CVE-2011-3389, 2011): Mitigated by recommending TLS 1.2 as the minimum; AEAD-only ciphers eliminate the CBC padding oracle.
  • CRIME (CVE-2012-4929, 2012): TLS compression is not supported in Go's crypto/tls.
  • Lucky13 (2013): AEAD-only cipher list eliminates CBC padding timing side-channels entirely.
  • FREAK (CVE-2015-0204, 2015): EXPORT-grade ciphers have never been supported in Go's crypto/tls.
  • LOGJAM (CVE-2015-4000, 2015): DHE key exchange is not offered by default in Go; only ECDHE is used.
  • Sweet32 (CVE-2016-2183, 2016): 3DES was removed from Go's default cipher list in Go 1.23 (August 2024) and is excluded from the recommended configuration.
  • ROBOT (2017): Static RSA key exchange is not offered in Go's crypto/tls; only ECDHE is used.
  • Downgrade attacks: Go's crypto/tls does not perform insecure version-fallback retries, and TLS 1.3 has built-in downgrade protection.

The following are not addressable through TLS configuration alone:

  • Heartbleed (CVE-2014-0160, 2014): Not applicable. Go's crypto/tls is an independent TLS implementation and was never affected by Heartbleed.
  • BREACH (CVE-2013-3587, 2013): Vault exposes a REST API over HTTPS. If HTTP response compression is enabled by a reverse proxy, apply BREACH countermeasures at that layer; TLS configuration cannot prevent it.
  • DROWN (CVE-2016-0800, 2016): Not applicable. Go's crypto/tls does not support SSLv2.

Verification

Start or restart Vault:

systemctl restart vault

Check Vault's status:

vault status

Test the TLS connection:

openssl s_client -connect vault.example.com:8200

Verify the certificate details:

curl --cacert /path/to/ca.pem https://vault.example.com:8200/v1/sys/health

Related Guides

View all Infrastructure guides →

Configured TLS? Now Monitor It.

Generator Labs alerts you before certificates expire, get revoked, or fail chain validation, across HTTPS, SMTPS, IMAPS, LDAPS, and more.

Certificate Monitoring →