# ASYNC-HVAC
[HashiCorp](https://hashicorp.com/) [Vault](https://www.vaultproject.io) API asyncio client for Python 3
![GitHub Actions Status](https://github.com/jamaalscarlett/async-hvac/actions/workflows/tests.yml/badge.svg)
![PYPI latest version](https://img.shields.io/pypi/v/async-hvac.svg)
## Getting started
### Installation
```bash
pip install async-hvac
```
or
```bash
pip install async-hvac[parser]
```
if you would like to be able to return parsed HCL data as a Python dict for methods that support it.
### Initialize the client
```python
import os
import async_hvac
# Using plaintext
client = async_hvac.AsyncClient()
client = async_hvac.AsyncClient(url='http://localhost:8200')
client = async_hvac.AsyncClient(url='http://localhost:8200', token=os.environ['VAULT_TOKEN'])
# Using TLS
client = async_hvac.AsyncClient(url='https://localhost:8200')
# Using TLS with client-side certificate authentication
client = async_hvac.AsyncClient(url='https://localhost:8200',
cert=('path/to/cert.pem', 'path/to/key.pem'))
# Skipping TLS verification entirely (should only be used for local development; unsafe for production clusters)
client = async_hvac.AsyncClient(url='https://localhost:8200', verify=False)
```
Note that you will have to close the client with `client.close()` in order to
avoid lingering open aiohttp.client.ClientSession's. An alternative is to open
the client in a `with`-statement:
```python
async with async_hvac.AsyncClient(url='https://localhost:8200') as client:
print(await client.read('secret/foo'))
```
### Read and write to secret backends
```python
await client.write('secret/foo', baz='bar', lease='1h')
print(await client.read('secret/foo'))
await client.delete('secret/foo')
```
### Authenticate to different auth backends
```python
# Token
client.token = 'MY_TOKEN'
assert await client.is_authenticated() # => True
# App ID
await client.auth_app_id('MY_APP_ID', 'MY_USER_ID')
# App Role
await client.auth_approle('MY_ROLE_ID', 'MY_SECRET_ID')
# AWS (IAM)
client.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY')
client.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY', 'MY_AWS_SESSION_TOKEN')
client.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY', role='MY_ROLE')
import boto3
session = boto3.Session()
credentials = session.get_credentials()
client.auth_aws_iam(credentials.access_key, credentials.secret_key, credentials.token)
# GitHub
await client.auth_github('MY_GITHUB_TOKEN')
# GCP (from GCE instance)
import aiohttp
VAULT_ADDR="https://vault.example.com:8200"
ROLE="example"
AUDIENCE_URL = VAULT_ADDR + "/vault/" + ROLE
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
FORMAT = 'full'
url = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience={}&format={}'.format(AUDIENCE_URL, FORMAT)
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=METADATA_HEADERS) as resp:
await client.auth_gcp(ROLE, await resp.text())
# Kubernetes (from k8s pod)
f = open('/var/run/secrets/kubernetes.io/serviceaccount/token')
jwt = f.read()
await client.auth_kubernetes("example", jwt)
# LDAP, Username & Password
await client.auth_ldap('MY_USERNAME', 'MY_PASSWORD')
await client.auth_userpass('MY_USERNAME', 'MY_PASSWORD')
# TLS
client = Client(cert=('path/to/cert.pem', 'path/to/key.pem'))
await client.auth_tls()
# Non-default mount point (available on all auth types)
await client.auth_userpass('MY_USERNAME', 'MY_PASSWORD', mount_point='CUSTOM_MOUNT_POINT')
# Authenticating without changing to new token (available on all auth types)
result = await client.auth_github('MY_GITHUB_TOKEN', use_token=False)
print(result['auth']['client_token']) # => u'NEW_TOKEN'
# Custom or unsupported auth type
params = {
'username': 'MY_USERNAME',
'password': 'MY_PASSWORD',
'custom_param': 'MY_CUSTOM_PARAM',
}
result = await client.auth('/v1/auth/CUSTOM_AUTH/login', json=params)
# Logout
await client.logout()
```
### Manage tokens
```python
token = await client.create_token(policies=['root'], lease='1h')
current_token = await client.lookup_token()
some_other_token = await client.lookup_token('xxx')
await client.revoke_token('xxx')
await client.revoke_token('yyy', orphan=True)
await client.revoke_token_prefix('zzz')
await client.renew_token('aaa')
```
### Managing tokens using accessors
```python
token = await client.create_token(policies=['root'], lease='1h')
token_accessor = token['auth']['accessor']
same_token = await client.lookup_token(token_accessor, accessor=True)
await client.revoke_token(token_accessor, accessor=True)
```
### Wrapping/unwrapping a token
```python
wrap = await client.create_token(policies=['root'], lease='1h', wrap_ttl='1m')
result = await self.client.unwrap(wrap['wrap_info']['token'])
```
### Manipulate auth backends
```python
backends = await client.list_auth_backends()
await client.enable_auth_backend('userpass', mount_point='customuserpass')
await client.disable_auth_backend('github')
```
### Manipulate secret backends
```python
backends = await client.list_secret_backends()
await client.enable_secret_backend('aws', mount_point='aws-us-east-1')
await client.disable_secret_backend('mysql')
await client.tune_secret_backend('generic', mount_point='test', default_lease_ttl='3600s', max_lease_ttl='8600s')
await client.get_secret_backend_tuning('generic', mount_point='test')
await client.remount_secret_backend('aws-us-east-1', 'aws-east')
```
### Manipulate policies
```python
policies = await client.list_policies() # => ['root']
policy = """
path "sys" {
policy = "deny"
}
path "secret" {
policy = "write"
}
path "secret/foo" {
policy = "read"
}
"""
await client.set_policy('myapp', policy)
await client.delete_policy('oldthing')
policy = await client.get_policy('mypolicy')
# Requires pyhcl to automatically parse HCL into a Python dictionary
policy = await client.get_policy('mypolicy', parse=True)
```
### Manipulate audit backends
```python
backends = await client.list_audit_backends()
options = {
'path': '/tmp/vault.log',
'log_raw': True,
}
await client.enable_audit_backend('file', options=options, name='somefile')
await client.disable_audit_backend('oldfile')
```
### Initialize and seal/unseal
```python
print(await client.is_initialized()) # => False
shares = 5
threshold = 3
result = await client.initialize(shares, threshold)
root_token = result['root_token']
keys = result['keys']
print(await client.is_initialized()) # => True
print(await client.is_sealed()) # => True
# unseal with individual keys
await client.unseal(keys[0])
await client.unseal(keys[1])
await client.unseal(keys[2])
# unseal with multiple keys until threshold met
await client.unseal_multi(keys)
print(await client.is_sealed()) # => False
await client.seal()
print(await client.is_sealed()) # => True
```
## Testing
Integration tests will automatically start a Vault server in the background. Just make sure
the latest `vault` binary is available in your `PATH`.
1. [Install Vault](https://vaultproject.io/docs/install/index.html) or execute `VAULT_BRANCH=release scripts/install-vault-release.sh`
2. [Install Tox](http://tox.readthedocs.org/en/latest/install.html)
3. Run tests: `make test`
## Contributing
Feel free to open pull requests with additional features or improvements!
## Vault versions
Tests are run against the last 3 versions of vault in line with Hashicorps support schedule. [LTS support](https://www.hashicorp.com/blog/vault-enterprise-long-term-support-lts-improves-operational-efficiency)
### Current supported versions
1.18.3
1.17.6
1.16.3
Raw data
{
"_id": null,
"home_page": "https://github.com/jamaalscarlett/async-hvac",
"name": "async-hvac",
"maintainer": "Jamaal Scarlett",
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "jamaalscarlett.github@gmail.com",
"keywords": "hashicorp, vault, hvac",
"author": "Lionel Zerbib",
"author_email": "lionel@alooma.io",
"download_url": "https://files.pythonhosted.org/packages/95/1a/785fc1a9c44257a6773396528c10fcf442a7f1e463691776cd337c0af564/async_hvac-0.7.2.tar.gz",
"platform": null,
"description": "# ASYNC-HVAC\n\n[HashiCorp](https://hashicorp.com/) [Vault](https://www.vaultproject.io) API asyncio client for Python 3\n\n![GitHub Actions Status](https://github.com/jamaalscarlett/async-hvac/actions/workflows/tests.yml/badge.svg) \n![PYPI latest version](https://img.shields.io/pypi/v/async-hvac.svg)\n\n## Getting started\n\n### Installation\n\n```bash\npip install async-hvac\n```\nor\n```bash\npip install async-hvac[parser]\n```\nif you would like to be able to return parsed HCL data as a Python dict for methods that support it.\n\n### Initialize the client\n\n```python\nimport os\n\nimport async_hvac\n\n# Using plaintext\nclient = async_hvac.AsyncClient()\nclient = async_hvac.AsyncClient(url='http://localhost:8200')\nclient = async_hvac.AsyncClient(url='http://localhost:8200', token=os.environ['VAULT_TOKEN'])\n\n# Using TLS\nclient = async_hvac.AsyncClient(url='https://localhost:8200')\n\n# Using TLS with client-side certificate authentication\nclient = async_hvac.AsyncClient(url='https://localhost:8200',\n cert=('path/to/cert.pem', 'path/to/key.pem'))\n # Skipping TLS verification entirely (should only be used for local development; unsafe for production clusters)\nclient = async_hvac.AsyncClient(url='https://localhost:8200', verify=False)\n```\n\nNote that you will have to close the client with `client.close()` in order to\navoid lingering open aiohttp.client.ClientSession's. An alternative is to open\nthe client in a `with`-statement:\n\n```python\nasync with async_hvac.AsyncClient(url='https://localhost:8200') as client:\n print(await client.read('secret/foo'))\n```\n\n### Read and write to secret backends\n\n```python\nawait client.write('secret/foo', baz='bar', lease='1h')\n\nprint(await client.read('secret/foo'))\n\nawait client.delete('secret/foo')\n```\n\n### Authenticate to different auth backends\n\n```python\n# Token\nclient.token = 'MY_TOKEN'\nassert await client.is_authenticated() # => True\n\n# App ID\nawait client.auth_app_id('MY_APP_ID', 'MY_USER_ID')\n\n# App Role\nawait client.auth_approle('MY_ROLE_ID', 'MY_SECRET_ID')\n\n# AWS (IAM)\nclient.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY')\nclient.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY', 'MY_AWS_SESSION_TOKEN')\nclient.auth_aws_iam('MY_AWS_ACCESS_KEY_ID', 'MY_AWS_SECRET_ACCESS_KEY', role='MY_ROLE')\n\nimport boto3\nsession = boto3.Session()\ncredentials = session.get_credentials()\nclient.auth_aws_iam(credentials.access_key, credentials.secret_key, credentials.token)\n\n# GitHub\nawait client.auth_github('MY_GITHUB_TOKEN')\n\n# GCP (from GCE instance)\nimport aiohttp\n\nVAULT_ADDR=\"https://vault.example.com:8200\"\nROLE=\"example\"\nAUDIENCE_URL = VAULT_ADDR + \"/vault/\" + ROLE\nMETADATA_HEADERS = {'Metadata-Flavor': 'Google'}\nFORMAT = 'full'\n\nurl = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience={}&format={}'.format(AUDIENCE_URL, FORMAT)\nasync with aiohttp.ClientSession() as session:\n async with session.get(url, headers=METADATA_HEADERS) as resp:\n await client.auth_gcp(ROLE, await resp.text())\n\n# Kubernetes (from k8s pod)\nf = open('/var/run/secrets/kubernetes.io/serviceaccount/token')\njwt = f.read()\nawait client.auth_kubernetes(\"example\", jwt)\n\n# LDAP, Username & Password\nawait client.auth_ldap('MY_USERNAME', 'MY_PASSWORD')\nawait client.auth_userpass('MY_USERNAME', 'MY_PASSWORD')\n\n# TLS\nclient = Client(cert=('path/to/cert.pem', 'path/to/key.pem'))\nawait client.auth_tls()\n\n# Non-default mount point (available on all auth types)\nawait client.auth_userpass('MY_USERNAME', 'MY_PASSWORD', mount_point='CUSTOM_MOUNT_POINT')\n\n# Authenticating without changing to new token (available on all auth types)\nresult = await client.auth_github('MY_GITHUB_TOKEN', use_token=False)\nprint(result['auth']['client_token']) # => u'NEW_TOKEN'\n\n# Custom or unsupported auth type\nparams = {\n 'username': 'MY_USERNAME',\n 'password': 'MY_PASSWORD',\n 'custom_param': 'MY_CUSTOM_PARAM',\n}\n\nresult = await client.auth('/v1/auth/CUSTOM_AUTH/login', json=params)\n\n# Logout\nawait client.logout()\n```\n\n### Manage tokens\n\n```python\ntoken = await client.create_token(policies=['root'], lease='1h')\n\ncurrent_token = await client.lookup_token()\nsome_other_token = await client.lookup_token('xxx')\n\nawait client.revoke_token('xxx')\nawait client.revoke_token('yyy', orphan=True)\n\nawait client.revoke_token_prefix('zzz')\n\nawait client.renew_token('aaa')\n```\n\n### Managing tokens using accessors\n\n```python\ntoken = await client.create_token(policies=['root'], lease='1h')\ntoken_accessor = token['auth']['accessor']\n\nsame_token = await client.lookup_token(token_accessor, accessor=True)\nawait client.revoke_token(token_accessor, accessor=True)\n```\n\n### Wrapping/unwrapping a token\n\n```python\nwrap = await client.create_token(policies=['root'], lease='1h', wrap_ttl='1m')\nresult = await self.client.unwrap(wrap['wrap_info']['token'])\n```\n\n### Manipulate auth backends\n\n```python\nbackends = await client.list_auth_backends()\n\nawait client.enable_auth_backend('userpass', mount_point='customuserpass')\nawait client.disable_auth_backend('github')\n```\n\n### Manipulate secret backends\n\n```python\nbackends = await client.list_secret_backends()\n\nawait client.enable_secret_backend('aws', mount_point='aws-us-east-1')\nawait client.disable_secret_backend('mysql')\n\nawait client.tune_secret_backend('generic', mount_point='test', default_lease_ttl='3600s', max_lease_ttl='8600s')\nawait client.get_secret_backend_tuning('generic', mount_point='test')\n\nawait client.remount_secret_backend('aws-us-east-1', 'aws-east')\n```\n\n### Manipulate policies\n\n```python\npolicies = await client.list_policies() # => ['root']\n\npolicy = \"\"\"\npath \"sys\" {\n policy = \"deny\"\n}\n\npath \"secret\" {\n policy = \"write\"\n}\n\npath \"secret/foo\" {\n policy = \"read\"\n}\n\"\"\"\n\nawait client.set_policy('myapp', policy)\n\nawait client.delete_policy('oldthing')\n\npolicy = await client.get_policy('mypolicy')\n\n# Requires pyhcl to automatically parse HCL into a Python dictionary\npolicy = await client.get_policy('mypolicy', parse=True)\n```\n\n### Manipulate audit backends\n\n```python\nbackends = await client.list_audit_backends()\n\noptions = {\n 'path': '/tmp/vault.log',\n 'log_raw': True,\n}\n\nawait client.enable_audit_backend('file', options=options, name='somefile')\nawait client.disable_audit_backend('oldfile')\n```\n\n### Initialize and seal/unseal\n\n```python\nprint(await client.is_initialized()) # => False\n\nshares = 5\nthreshold = 3\n\nresult = await client.initialize(shares, threshold)\n\nroot_token = result['root_token']\nkeys = result['keys']\n\nprint(await client.is_initialized()) # => True\n\nprint(await client.is_sealed()) # => True\n\n# unseal with individual keys\nawait client.unseal(keys[0])\nawait client.unseal(keys[1])\nawait client.unseal(keys[2])\n\n# unseal with multiple keys until threshold met\nawait client.unseal_multi(keys)\n\nprint(await client.is_sealed()) # => False\n\nawait client.seal()\n\nprint(await client.is_sealed()) # => True\n```\n\n## Testing\n\nIntegration tests will automatically start a Vault server in the background. Just make sure\nthe latest `vault` binary is available in your `PATH`.\n\n1. [Install Vault](https://vaultproject.io/docs/install/index.html) or execute `VAULT_BRANCH=release scripts/install-vault-release.sh`\n2. [Install Tox](http://tox.readthedocs.org/en/latest/install.html)\n3. Run tests: `make test`\n\n## Contributing\n\nFeel free to open pull requests with additional features or improvements!\n\n\n## Vault versions\nTests are run against the last 3 versions of vault in line with Hashicorps support schedule. [LTS support](https://www.hashicorp.com/blog/vault-enterprise-long-term-support-lts-improves-operational-efficiency)\n\n### Current supported versions\n1.18.3\n\n1.17.6\n\n1.16.3\n",
"bugtrack_url": null,
"license": null,
"summary": "HashiCorp Vault API python 3.9+ client using asyncio.",
"version": "0.7.2",
"project_urls": {
"Download": "https://github.com/jamaalscarlett/async-hvac/tarball/master",
"Homepage": "https://github.com/jamaalscarlett/async-hvac"
},
"split_keywords": [
"hashicorp",
" vault",
" hvac"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "2bf3ff239740534a860b3c1b1660af7e264edf4cd9aaf1f15aa2372eb2f41fe7",
"md5": "f6ca9a7bdf591727f28075c6a846ccc7",
"sha256": "18f1db2e4bc444a7a8e4a721dc0b4ebbdfb84b6bf1a9031edece25442cd563ec"
},
"downloads": -1,
"filename": "async_hvac-0.7.2-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "f6ca9a7bdf591727f28075c6a846ccc7",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=3.9",
"size": 20954,
"upload_time": "2025-01-22T14:42:26",
"upload_time_iso_8601": "2025-01-22T14:42:26.190557Z",
"url": "https://files.pythonhosted.org/packages/2b/f3/ff239740534a860b3c1b1660af7e264edf4cd9aaf1f15aa2372eb2f41fe7/async_hvac-0.7.2-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "951a785fc1a9c44257a6773396528c10fcf442a7f1e463691776cd337c0af564",
"md5": "3abe7de7792b49986dfdc1e1326e6fd9",
"sha256": "5c6881c5dd08c2610bacd735b17bd339c61066f7c2ac943b07a0d9ac4a683ece"
},
"downloads": -1,
"filename": "async_hvac-0.7.2.tar.gz",
"has_sig": false,
"md5_digest": "3abe7de7792b49986dfdc1e1326e6fd9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 23354,
"upload_time": "2025-01-22T14:42:27",
"upload_time_iso_8601": "2025-01-22T14:42:27.948059Z",
"url": "https://files.pythonhosted.org/packages/95/1a/785fc1a9c44257a6773396528c10fcf442a7f1e463691776cd337c0af564/async_hvac-0.7.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-22 14:42:27",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jamaalscarlett",
"github_project": "async-hvac",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "aiohappyeyeballs",
"specs": [
[
"==",
"2.4.4"
]
]
},
{
"name": "aiohttp",
"specs": [
[
"==",
"3.11.11"
]
]
},
{
"name": "aiosignal",
"specs": [
[
"==",
"1.3.2"
]
]
},
{
"name": "attrs",
"specs": [
[
"==",
"24.3.0"
]
]
},
{
"name": "frozenlist",
"specs": [
[
"==",
"1.5.0"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.10"
]
]
},
{
"name": "multidict",
"specs": [
[
"==",
"6.1.0"
]
]
},
{
"name": "propcache",
"specs": [
[
"==",
"0.2.1"
]
]
},
{
"name": "pyhcl",
"specs": [
[
"==",
"0.4.5"
]
]
},
{
"name": "yarl",
"specs": [
[
"==",
"1.18.3"
]
]
}
],
"tox": true,
"lcname": "async-hvac"
}