Securing a REST call to Vault Secrets management

Viewed 496

Been trying to figure out how to do this for awhile. Essentially, Vault does not have a secure option for its REST calls. I want to make these rest calls encrypted from as close between point a and b as possible. My thoughts have been the following:

  • Use an SSH tunnel
  • Use a TLS tunnel like Stunnel

I currently have Vault in a Docker container, so that’s something else to mention. Has anyone encountered this situation, and how did you deal with it?

UPDATE: So, using the Python API (HVAC), I am getting the following error:

requests.exceptions.SSLError: HTTPSConnectionPool(host='0.0.0.0',
port=8200): Max retries exceeded with url: /v1/secret (Caused by
SSLError(SSLError("bad handshake: Error([('SSL routines', 'ssl3_get_record',
'wrong version number')],)",),))

Using the following commands:

import os
import hvac
client = hvac.Client(url='https://0.0.0.0:8200', token='my-token-here')
3 Answers

Vault has TLS enabled by default, thus all your REST calls are encrypted already. If you are having trouble using https, have a look at the documentation of VAULT_CACERT and VAULT_CAPATH environment variables.

from vault's documentation.

VAULT_CACERT

Path to a PEM-encoded CA certificate file on the local disk. This file is used to verify the Vault server's SSL certificate. This environment variable takes precedence over VAULT_CAPATH.

VAULT_CAPATH Path to a directory of PEM-encoded CA certificate files on the local disk. These certificates are used to verify the Vault server's SSL certificate.

You can use tools like tcpdump or wireshark to make sure that your requests are indeed encrypted.

To elaborate for Vault running in a container, you need to create a configuration file for Vault that contains something similar to this this (Chef/Ruby code):

config_content = %(
  "storage": {
     ...
  },
  "default_lease_ttl": "768h",
  "max_lease_ttl": "8766h",
  "listener": [
    {"tcp": {
      "address": "0.0.0.0:8200",
      "tls_disable": 0,
      "tls_cert_file": "/vault/certs/my-cert-combined.pem",
      "tls_key_file": "/vault/certs/my-cert.key"
    }}],
  "log_level": "info"
 )

Especially the listener portion. Make your backend storage whatever you want to use (not the Dev default of in-memory!).

Note you will need to get a valid certificate and its private key also in the volume bound into the container.

Store this configuration file in a directory that gets bound inside the container to the path /vault/config. I use /var/vault/config on my host. For example (more Ruby/Chef):

docker_container 'vault' do
  container_name 'vault'
  tag 'latest'
  port '8200:8200'
  cap_add ['IPC_LOCK']
  restart_policy 'always'
  volumes ['/var/vault:/vault']
  command 'vault server -config /vault/config'
  action :run_if_missing
end

That command tells Vault to look in /vault/config and it should find your config file there, with a .json extension. Note it is important to have the config file listener->tcp->address be 0.0.0.0, rather than 127.0.0.1, because Vault will not resolve external accesses properly.

Then Vault will startup with TLS encryption on all transactions. Define VAULT_ADDR to have https://your-host.com:8200 and away you go.

In my case, I've been testing it on my local environment. So instead of calling the secured https: https://localhost:8200, I called regular http: http://localhost:8200. This solved the error.

Related