hvac

Header image

HashiCorp Vault API client for Python 3.x

Build Lint codecov Documentation Status PyPI version Twitter - @python_hvac Gitter chat

Tested against the latest release, HEAD ref, and 3 previous minor versions (counting back from the latest release) of Vault. Current official support covers Vault v1.4.7 or later.

NOTE: Support for EOL Python versions will be dropped at the end of 2022. Starting in 2023, hvac will track with the CPython EOL dates.

Installation

pip install hvac

If you would like to be able to return parsed HCL data as a Python dict for methods that support it:

pip install "hvac[parser]"

Getting Started

Initialize the Client

Using TLS:

>>> client = hvac.Client(url='https://localhost:8200')
>>> client.is_authenticated()
True

Using TLS with client-side certificate authentication:

>>> client = hvac.Client(
...     url='https://localhost:8200',
...     token=os.environ['VAULT_TOKEN'],
...     cert=(client_cert_path, client_key_path),
...     verify=server_cert_path,
... )
>>> client.is_authenticated()
True

Using Vault Enterprise namespace:

>>> client = hvac.Client(
...     url='https://localhost:8200',
...     namespace=os.getenv('VAULT_NAMESPACE'),
... )

Using plaintext / HTTP (not recommended for anything other than development work):

>>> client = hvac.Client(url='http://localhost:8200')

Vault Cluster - Initialize and Seal/Unseal

>>> client.sys.is_initialized()
False

>>> shares = 5
>>> threshold = 3
>>> result = client.sys.initialize(shares, threshold)
>>> root_token = result['root_token']
>>> keys = result['keys']
>>> client.sys.is_initialized()
True

>>> client.token = root_token

>>> client.sys.is_sealed()
True
>>> # Unseal a Vault cluster with individual keys
>>> unseal_response1 = client.sys.submit_unseal_key(keys[0])
>>> unseal_response2 = client.sys.submit_unseal_key(keys[1])
>>> unseal_response3 = client.sys.submit_unseal_key(keys[2])
>>> client.sys.is_sealed()
False
>>> # Seal a previously unsealed Vault cluster.
>>> client.sys.seal()
<Response [204]>
>>> client.sys.is_sealed()
True

>>> # Unseal with multiple keys until threshold met
>>> unseal_response = client.sys.submit_unseal_keys(keys)

>>> client.sys.is_sealed()
False

Read and write to secrets engines

Note

Vault currently defaults the secret/ path to the KV secrets engine version 2 automatically when the Vault server is started in β€œdev” mode.

Note

Starting with Vault v1.1.0, _no_ KV secrets engine is mounted by default. I.e., outside of dev mode, a KV engine mounted under path secret/ must be explicitly enabled before use.

KV Secrets Engine - Version 2

 >>> # Write a k/v pair under path: secret/foo
 >>> create_response = client.secrets.kv.v2.create_or_update_secret(
 ...     path='foo',
 ...     secret=dict(baz='bar'),
 ... )
 >>>
 >>> # Read the data written under path: secret/foo
 >>> read_response = client.secrets.kv.read_secret_version(path='foo')
 >>> print('Value under path "secret/foo" / key "baz": {val}'.format(
 ...     val=read_response['data']['data']['baz'],
 ... ))
 Value under path "secret/foo" / key "baz": bar
 >>>
 >>> # Delete all metadata/versions for path: secret/foo
 >>> client.secrets.kv.delete_metadata_and_all_versions('foo')
 <Response [204]>

KV Secrets Engine - Version 1

Preferred usage:

 >>> create_response = client.secrets.kv.v1.create_or_update_secret('foo', secret=dict(baz='bar'))
 >>> read_response = client.secrets.kv.v1.read_secret('foo')
 >>> print('Value under path "secret/foo" / key "baz": {val}'.format(
 ...     val=read_response['data']['baz'],
 ... ))
 Value under path "secret/foo" / key "baz": bar
 >>> delete_response = client.secrets.kv.v1.delete_secret('foo')

Authentication

Basic Token Authentication

# Token
>>> client.token = os.environ['VAULT_TOKEN']
>>> client.is_authenticated()
True

LDAP Authentication Example

>>> client = hvac.Client(url='https://localhost:8200')
>>> # LDAP, getpass -> user/password, bring in LDAP3 here for teststup?
>>> login_response = client.auth.ldap.login(
...     username=os.environ['LDAP_USERNAME'],
...     password=os.environ['LDAP_PASSWORD'],
... )
>>> client.is_authenticated()
True
>>> print('The client token returned from the LDAP auth method is: {token}'.format(
...     token=login_response['auth']['client_token']
... ))
The client token returned from the LDAP auth method is: ...