Name | vault-exodus JSON |
Version |
0.1.3.7
JSON |
| download |
home_page | None |
Summary | A community-driven tool for migrating HashiCorp Vault secrets between KV engines (v1/v2). Use at your own RISK |
upload_time | 2025-02-06 11:12:56 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.7 |
license | MIT |
keywords |
vault
hashicorp
migration
secrets
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Exodus
```
███████╗██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗
██╔════╝╚██╗██╔╝██╔═══██╗██╔══██╗██║ ██║██╔════╝
█████╗ ╚███╔╝ ██║ ██║██║ ██║██║ ██║███████╗
██╔══╝ ██╔██╗ ██║ ██║██║ ██║██║ ██║╚════██║
███████╗██╔╝ ██╗╚██████╔╝██████╔╝╚██████╔╝███████║
╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
```
# Exodus
A Python tool for migrating secrets between HashiCorp Vault clusters. Supports copying secrets from KV v1/v2 mounts between Vault instances.
> **Disclaimer**: This is not an official HashiCorp tool. Community-created project for Vault secrets migration. Use at your own risk.
## Features
- KV v1 and v2 mount support
- Recursive secret listing with subpath preservation
- Optional root path modifications
- Dry-run mode for operation preview
- Configurable rate limiting
- Vault Enterprise namespace support
- Flexible SSL/TLS verification with CA certificates
## Installation
```bash
pip install vault-exodus # Latest version
pip install vault-exodus==0.1.1 # Specific version
```
### Requirements
- Python 3.7+ (Recommended)
- Requests
- tqdm
Dependencies are automatically installed via pip.
## Usage
### CLI
```bash
exodus [OPTIONS]
```
#### Key Arguments
| Argument | Description | Default |
|----------|-------------|---------|
| `--vault-a-addr` | Source Vault URL | http://localhost:8200 |
| `--vault-a-token` | Source Vault token | Required |
| `--vault-a-mount` | Source KV mount name | secret |
| `--vault-a-path-root` | Source root path | myapp |
| `--vault-b-addr` | Destination Vault URL | http://localhost:8200 |
| `--vault-b-token` | Destination Vault token | Required |
| `--vault-b-mount` | Destination KV mount name | secret |
| `--vault-b-path-root` | Destination root path | myapp-copied |
[View complete arguments table in documentation]
### Example
```bash
exodus \
--vault-a-addr="https://source-vault.example.com" \
--vault-a-token="s.ABCD1234" \
--vault-a-mount="secret" \
--vault-a-path-root="myapp" \
--vault-a-namespace="admin" \
--vault-a-kv-version="2" \
--vault-b-addr="https://destination-vault.example.com" \
--vault-b-token="s.EFGH5678" \
--vault-b-mount="secret" \
--vault-b-path-root="myapp-copied" \
--vault-b-kv-version="2" \
--rate-limit=0.5 \
--dry-run
```
### Python Library Usage
## kv secrets engine
```python
from exodus.kv_migrator import list_secrets, read_secret, write_secret
from tqdm import tqdm
import time
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
def simple_migrate(
src_addr, src_token, src_mount, src_root, src_kv_version, src_namespace,
dst_addr, dst_token, dst_mount, dst_root, dst_kv_version, dst_namespace,
dry_run=False, rate_limit=1.0
):
logging.info(f"Listing secrets in '{src_root}' from {src_addr} (KV v{src_kv_version})")
secret_paths = list_secrets(
vault_addr=src_addr,
token=src_token,
mount=src_mount,
path=src_root,
kv_version=src_kv_version,
namespace=src_namespace,
verify=False
)
logging.info(f"Found {len(secret_paths)} secrets to copy")
failed_copies = []
for spath in tqdm(secret_paths, desc="Copying secrets"):
try:
data = read_secret(
vault_addr=src_addr,
token=src_token,
mount=src_mount,
path=spath,
kv_version=src_kv_version,
namespace=src_namespace,
verify=False
)
if not data:
logging.debug(f"No data for '{spath}'; skipping")
continue
if spath.startswith(src_root + "/"):
relative = spath[len(src_root)+1:]
dpath = f"{dst_root}/{relative}"
else:
dpath = f"{dst_root}/{spath}"
if dry_run:
logging.info(f"[Dry Run] Would copy '{spath}' -> '{dpath}'")
else:
write_secret(
vault_addr=dst_addr,
token=dst_token,
mount=dst_mount,
path=dpath,
secret_data=data,
kv_version=dst_kv_version,
namespace=dst_namespace,
verify=False
)
logging.info(f"Copied '{spath}' -> '{dpath}'")
if rate_limit > 0:
time.sleep(rate_limit)
except Exception as e:
failed_copies.append((spath, str(e)))
logging.error(f"Failed to copy '{spath}': {e}")
if failed_copies:
logging.error("\nSome secrets failed to copy:")
for path, error in failed_copies:
logging.error(f" - {path}: {error}")
def main():
# Example usage
simple_migrate(
src_addr="http://localhost:8200",
src_token="root",
src_mount="secret",
src_root="myapp",
src_kv_version="2",
src_namespace="admin",
dst_addr="http://localhost:8200",
dst_token="root",
dst_mount="secret",
dst_root="myapp-copied",
dst_kv_version="2",
dst_namespace="admin",
dry_run=True
)
if __name__ == "__main__":
main()
```
## list namespaces
depth control works the following way:
max_depth=1: Only direct children
max_depth=2: Children and grandchildren
max_depth=0: All levels (unlimited)
```python
import os
import logging
from exodus.namespace import list_namespaces
# Set up logging
logging.basicConfig(level=logging.INFO)
def test_namespace_depths():
# Get environment variables
vault_addr = os.getenv("VAULT_ADDR", "http://localhost:8200")
vault_token = os.getenv("VAULT_TOKEN")
base_namespace = os.getenv("VAULT_NAMESPACE", "admin")
max_depth = int(os.getenv("VAULT_MAX_DEPTH", "1"))
if not vault_token:
raise ValueError("VAULT_TOKEN environment variable must be set")
print(f"Testing with:")
print(f"Base namespace: '{base_namespace}'")
print(f"Max depth: {max_depth}")
# Call the library function
namespaces = list_namespaces(
vault_addr=vault_addr,
token=vault_token,
base_namespace=base_namespace,
max_depth=max_depth
)
print("\n=== Namespaces Found ===")
if not namespaces:
print("No namespaces found.")
else:
for ns in sorted(namespaces):
depth = len(ns.split('/')) - 1
indent = " " * depth
print(f"{indent}- {ns}")
if __name__ == "__main__":
test_namespace_depths()
```
Remember to
```bash
# First set your environment variables if not already set
export VAULT_ADDR="your-vault-address"
export VAULT_TOKEN="your-token"
export VAULT_NAMESPACE="admin"
export VAULT_MAX_DEPTH=1
```
## list auth methods and secret engines
```python
import os
import logging
from typing import Dict, Any
from exodus.auth import list_auth_methods
from exodus.secret import list_secret_engines
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s'
)
def test_vault_backends(
vault_addr: str,
token: str,
namespace: str = "",
verify: bool = True
) -> Dict[str, Dict[str, Any]]:
"""
Test function to list both auth methods and secret engines.
Args:
vault_addr: Vault server address
token: Vault token
namespace: Namespace to inspect
verify: SSL verification
"""
results = {
'auth_methods': {},
'secret_engines': {}
}
try:
# List auth methods
auth_methods = list_auth_methods(
vault_addr=vault_addr,
token=token,
namespace=namespace,
verify=verify
)
results['auth_methods'] = auth_methods
# List secret engines
secret_engines = list_secret_engines(
vault_addr=vault_addr,
token=token,
namespace=namespace,
verify=verify
)
results['secret_engines'] = secret_engines
except Exception as e:
logging.error(f"Test failed: {str(e)}")
return results
def main():
# Get environment variables
VAULT_ADDR = os.getenv("VAULT_ADDR", "http://localhost:8200")
VAULT_TOKEN = os.getenv("VAULT_TOKEN")
BASE_NAMESPACE = os.getenv("VAULT_NAMESPACE", "admin")
SKIP_VERIFY = os.getenv("VAULT_SKIP_VERIFY", "false").lower() == "true"
# Validate required environment variables
if not VAULT_TOKEN:
raise ValueError("VAULT_TOKEN environment variable must be set")
# Log configuration
logging.info(f"Vault address: {VAULT_ADDR}")
logging.info(f"Base namespace: '{BASE_NAMESPACE}' (empty means root)")
logging.info(f"SSL verification: {'disabled' if SKIP_VERIFY else 'enabled'}")
try:
results = test_vault_backends(
vault_addr=VAULT_ADDR,
token=VAULT_TOKEN,
namespace=BASE_NAMESPACE,
verify=not SKIP_VERIFY
)
# Print auth methods
print("\n=== Enabled Auth Methods ===")
if not results['auth_methods']:
print(" No auth methods found")
else:
for path, config in sorted(results['auth_methods'].items()):
print(f" - {path}: {config['type']}")
# Optionally print more details
if 'description' in config:
print(f" Description: {config['description']}")
# Print secret engines
print("\n=== Enabled Secret Engines ===")
if not results['secret_engines']:
print(" No secret engines found")
else:
for path, config in sorted(results['secret_engines'].items()):
print(f" - {path}: {config['type']}")
# Optionally print more details
if 'description' in config:
print(f" Description: {config['description']}")
except Exception as e:
logging.error(f"Failed to list backends: {str(e)}")
raise
if __name__ == "__main__":
main()
```
```
export VAULT_ADDR="https://vault.example.com:8200"
export VAULT_TOKEN="your-token"
export VAULT_NAMESPACE="admin"
export VAULT_SKIP_VERIFY="true" # Optional for testing
```
## Best Practices
- Test migrations with `--dry-run` before production use
- Increase `--rate-limit` for large datasets
- Use appropriate CA certificates in secure environments
- Verify token permissions (read on source, write on destination)
## Contributing
Contributions welcome! Please feel free to submit pull requests or issues on GitHub.
## License
MIT License. See [LICENSE](LICENSE) file for details.
Again, note: This is not an official HashiCorp tool. It is a community-driven script created to help anyone needing to migrate secrets between Vault instances. Always confirm it meets your security and compliance requirements before use. Use it at your own risk.
Raw data
{
"_id": null,
"home_page": null,
"name": "vault-exodus",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "vault, hashicorp, migration, secrets",
"author": null,
"author_email": "AndyLow <memebotandres@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/14/17/3d4943c718c95a75432b91735bb647a8baec26e7bc125adabbbe58f6f972/vault_exodus-0.1.3.7.tar.gz",
"platform": null,
"description": "# Exodus\n\n```\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d\u255a\u2588\u2588\u2557\u2588\u2588\u2554\u255d\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d\n\u2588\u2588\u2588\u2588\u2588\u2557 \u255a\u2588\u2588\u2588\u2554\u255d \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u2588\u2588\u2554\u2550\u2550\u255d \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u255a\u2550\u2550\u2550\u2550\u2588\u2588\u2551\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255d \u2588\u2588\u2557\u255a\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d\u255a\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u255d \u255a\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n```\n# Exodus\n\nA Python tool for migrating secrets between HashiCorp Vault clusters. Supports copying secrets from KV v1/v2 mounts between Vault instances.\n\n> **Disclaimer**: This is not an official HashiCorp tool. Community-created project for Vault secrets migration. Use at your own risk.\n\n## Features\n\n- KV v1 and v2 mount support\n- Recursive secret listing with subpath preservation\n- Optional root path modifications\n- Dry-run mode for operation preview\n- Configurable rate limiting\n- Vault Enterprise namespace support\n- Flexible SSL/TLS verification with CA certificates\n\n## Installation\n\n```bash\npip install vault-exodus # Latest version\npip install vault-exodus==0.1.1 # Specific version\n```\n\n### Requirements\n- Python 3.7+ (Recommended)\n- Requests\n- tqdm\n\nDependencies are automatically installed via pip.\n\n## Usage\n\n### CLI\n\n```bash\nexodus [OPTIONS]\n```\n\n#### Key Arguments\n\n| Argument | Description | Default |\n|----------|-------------|---------|\n| `--vault-a-addr` | Source Vault URL | http://localhost:8200 |\n| `--vault-a-token` | Source Vault token | Required |\n| `--vault-a-mount` | Source KV mount name | secret |\n| `--vault-a-path-root` | Source root path | myapp |\n| `--vault-b-addr` | Destination Vault URL | http://localhost:8200 |\n| `--vault-b-token` | Destination Vault token | Required |\n| `--vault-b-mount` | Destination KV mount name | secret |\n| `--vault-b-path-root` | Destination root path | myapp-copied |\n\n[View complete arguments table in documentation]\n\n### Example\n\n```bash\nexodus \\\n --vault-a-addr=\"https://source-vault.example.com\" \\\n --vault-a-token=\"s.ABCD1234\" \\\n --vault-a-mount=\"secret\" \\\n --vault-a-path-root=\"myapp\" \\\n --vault-a-namespace=\"admin\" \\\n --vault-a-kv-version=\"2\" \\\n --vault-b-addr=\"https://destination-vault.example.com\" \\\n --vault-b-token=\"s.EFGH5678\" \\\n --vault-b-mount=\"secret\" \\\n --vault-b-path-root=\"myapp-copied\" \\\n --vault-b-kv-version=\"2\" \\\n --rate-limit=0.5 \\\n --dry-run\n```\n\n### Python Library Usage\n\n## kv secrets engine \n\n```python\nfrom exodus.kv_migrator import list_secrets, read_secret, write_secret\nfrom tqdm import tqdm\nimport time\nimport logging\n\nlogging.basicConfig(\n level=logging.INFO,\n format=\"%(asctime)s [%(levelname)s] %(message)s\"\n)\n\ndef simple_migrate(\n src_addr, src_token, src_mount, src_root, src_kv_version, src_namespace,\n dst_addr, dst_token, dst_mount, dst_root, dst_kv_version, dst_namespace,\n dry_run=False, rate_limit=1.0\n):\n logging.info(f\"Listing secrets in '{src_root}' from {src_addr} (KV v{src_kv_version})\")\n \n secret_paths = list_secrets(\n vault_addr=src_addr,\n token=src_token,\n mount=src_mount,\n path=src_root,\n kv_version=src_kv_version,\n namespace=src_namespace,\n verify=False\n )\n\n logging.info(f\"Found {len(secret_paths)} secrets to copy\")\n failed_copies = []\n \n for spath in tqdm(secret_paths, desc=\"Copying secrets\"):\n try:\n data = read_secret(\n vault_addr=src_addr,\n token=src_token,\n mount=src_mount,\n path=spath,\n kv_version=src_kv_version,\n namespace=src_namespace,\n verify=False\n )\n if not data:\n logging.debug(f\"No data for '{spath}'; skipping\")\n continue\n \n if spath.startswith(src_root + \"/\"):\n relative = spath[len(src_root)+1:]\n dpath = f\"{dst_root}/{relative}\"\n else:\n dpath = f\"{dst_root}/{spath}\"\n \n if dry_run:\n logging.info(f\"[Dry Run] Would copy '{spath}' -> '{dpath}'\")\n else:\n write_secret(\n vault_addr=dst_addr,\n token=dst_token,\n mount=dst_mount,\n path=dpath,\n secret_data=data,\n kv_version=dst_kv_version,\n namespace=dst_namespace,\n verify=False\n )\n logging.info(f\"Copied '{spath}' -> '{dpath}'\")\n \n if rate_limit > 0:\n time.sleep(rate_limit)\n \n except Exception as e:\n failed_copies.append((spath, str(e)))\n logging.error(f\"Failed to copy '{spath}': {e}\")\n\n if failed_copies:\n logging.error(\"\\nSome secrets failed to copy:\")\n for path, error in failed_copies:\n logging.error(f\" - {path}: {error}\")\n\ndef main():\n # Example usage\n simple_migrate(\n src_addr=\"http://localhost:8200\",\n src_token=\"root\",\n src_mount=\"secret\", \n src_root=\"myapp\",\n src_kv_version=\"2\",\n src_namespace=\"admin\",\n dst_addr=\"http://localhost:8200\",\n dst_token=\"root\",\n dst_mount=\"secret\",\n dst_root=\"myapp-copied\",\n dst_kv_version=\"2\",\n dst_namespace=\"admin\",\n dry_run=True\n )\n\nif __name__ == \"__main__\":\n main()\n```\n\n## list namespaces \ndepth control works the following way:\n\nmax_depth=1: Only direct children\nmax_depth=2: Children and grandchildren\nmax_depth=0: All levels (unlimited)\n```python\nimport os\nimport logging\nfrom exodus.namespace import list_namespaces\n\n# Set up logging\nlogging.basicConfig(level=logging.INFO)\n\ndef test_namespace_depths():\n # Get environment variables\n vault_addr = os.getenv(\"VAULT_ADDR\", \"http://localhost:8200\")\n vault_token = os.getenv(\"VAULT_TOKEN\")\n base_namespace = os.getenv(\"VAULT_NAMESPACE\", \"admin\")\n max_depth = int(os.getenv(\"VAULT_MAX_DEPTH\", \"1\"))\n \n if not vault_token:\n raise ValueError(\"VAULT_TOKEN environment variable must be set\")\n\n print(f\"Testing with:\")\n print(f\"Base namespace: '{base_namespace}'\")\n print(f\"Max depth: {max_depth}\")\n\n # Call the library function\n namespaces = list_namespaces(\n vault_addr=vault_addr,\n token=vault_token,\n base_namespace=base_namespace,\n max_depth=max_depth\n )\n\n print(\"\\n=== Namespaces Found ===\")\n if not namespaces:\n print(\"No namespaces found.\")\n else:\n for ns in sorted(namespaces):\n depth = len(ns.split('/')) - 1\n indent = \" \" * depth\n print(f\"{indent}- {ns}\")\n\nif __name__ == \"__main__\":\n test_namespace_depths()\n ```\n\nRemember to\n ```bash\n# First set your environment variables if not already set\nexport VAULT_ADDR=\"your-vault-address\"\nexport VAULT_TOKEN=\"your-token\"\nexport VAULT_NAMESPACE=\"admin\"\nexport VAULT_MAX_DEPTH=1\n\n ```\n## list auth methods and secret engines \n```python\nimport os\nimport logging\nfrom typing import Dict, Any\nfrom exodus.auth import list_auth_methods\nfrom exodus.secret import list_secret_engines\n\n# Configure logging\nlogging.basicConfig(\n level=logging.INFO,\n format='%(asctime)s [%(levelname)s] %(message)s'\n)\n\ndef test_vault_backends(\n vault_addr: str,\n token: str,\n namespace: str = \"\",\n verify: bool = True\n) -> Dict[str, Dict[str, Any]]:\n \"\"\"\n Test function to list both auth methods and secret engines.\n \n Args:\n vault_addr: Vault server address\n token: Vault token\n namespace: Namespace to inspect\n verify: SSL verification\n \"\"\"\n results = {\n 'auth_methods': {},\n 'secret_engines': {}\n }\n\n try:\n # List auth methods\n auth_methods = list_auth_methods(\n vault_addr=vault_addr,\n token=token,\n namespace=namespace,\n verify=verify\n )\n results['auth_methods'] = auth_methods\n\n # List secret engines\n secret_engines = list_secret_engines(\n vault_addr=vault_addr,\n token=token,\n namespace=namespace,\n verify=verify\n )\n results['secret_engines'] = secret_engines\n\n except Exception as e:\n logging.error(f\"Test failed: {str(e)}\")\n\n return results\n\ndef main():\n # Get environment variables\n VAULT_ADDR = os.getenv(\"VAULT_ADDR\", \"http://localhost:8200\")\n VAULT_TOKEN = os.getenv(\"VAULT_TOKEN\")\n BASE_NAMESPACE = os.getenv(\"VAULT_NAMESPACE\", \"admin\")\n SKIP_VERIFY = os.getenv(\"VAULT_SKIP_VERIFY\", \"false\").lower() == \"true\"\n\n # Validate required environment variables\n if not VAULT_TOKEN:\n raise ValueError(\"VAULT_TOKEN environment variable must be set\")\n\n # Log configuration\n logging.info(f\"Vault address: {VAULT_ADDR}\")\n logging.info(f\"Base namespace: '{BASE_NAMESPACE}' (empty means root)\")\n logging.info(f\"SSL verification: {'disabled' if SKIP_VERIFY else 'enabled'}\")\n\n try:\n results = test_vault_backends(\n vault_addr=VAULT_ADDR,\n token=VAULT_TOKEN,\n namespace=BASE_NAMESPACE,\n verify=not SKIP_VERIFY\n )\n\n # Print auth methods\n print(\"\\n=== Enabled Auth Methods ===\")\n if not results['auth_methods']:\n print(\" No auth methods found\")\n else:\n for path, config in sorted(results['auth_methods'].items()):\n print(f\" - {path}: {config['type']}\")\n # Optionally print more details\n if 'description' in config:\n print(f\" Description: {config['description']}\")\n\n # Print secret engines\n print(\"\\n=== Enabled Secret Engines ===\")\n if not results['secret_engines']:\n print(\" No secret engines found\")\n else:\n for path, config in sorted(results['secret_engines'].items()):\n print(f\" - {path}: {config['type']}\")\n # Optionally print more details\n if 'description' in config:\n print(f\" Description: {config['description']}\")\n\n except Exception as e:\n logging.error(f\"Failed to list backends: {str(e)}\")\n raise\n\nif __name__ == \"__main__\":\n main()\n ```\n```\nexport VAULT_ADDR=\"https://vault.example.com:8200\"\nexport VAULT_TOKEN=\"your-token\"\nexport VAULT_NAMESPACE=\"admin\"\nexport VAULT_SKIP_VERIFY=\"true\" # Optional for testing\n ```\n\n## Best Practices\n\n- Test migrations with `--dry-run` before production use\n- Increase `--rate-limit` for large datasets\n- Use appropriate CA certificates in secure environments\n- Verify token permissions (read on source, write on destination)\n\n## Contributing\n\nContributions welcome! Please feel free to submit pull requests or issues on GitHub.\n\n## License\n\nMIT License. See [LICENSE](LICENSE) file for details.\n\nAgain, note: This is not an official HashiCorp tool. It is a community-driven script created to help anyone needing to migrate secrets between Vault instances. Always confirm it meets your security and compliance requirements before use. Use it at your own risk.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A community-driven tool for migrating HashiCorp Vault secrets between KV engines (v1/v2). Use at your own RISK",
"version": "0.1.3.7",
"project_urls": {
"Source Code": "https://github.com/andylow92/exodus",
"Tracker": "https://github.com/andylow92/exodus/issues"
},
"split_keywords": [
"vault",
" hashicorp",
" migration",
" secrets"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "727a1f445e409d848cfce58972914a0225aa5e2c2adfe4c5524e7b05d834a120",
"md5": "aa675ceba4231cafd3060288ecf582e2",
"sha256": "88936ea02dab7158c3aefb4d23e8a03bc99b4a1952416a18f0f0f3e1e9498af0"
},
"downloads": -1,
"filename": "vault_exodus-0.1.3.7-py3-none-any.whl",
"has_sig": false,
"md5_digest": "aa675ceba4231cafd3060288ecf582e2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 13226,
"upload_time": "2025-02-06T11:12:55",
"upload_time_iso_8601": "2025-02-06T11:12:55.242981Z",
"url": "https://files.pythonhosted.org/packages/72/7a/1f445e409d848cfce58972914a0225aa5e2c2adfe4c5524e7b05d834a120/vault_exodus-0.1.3.7-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "14173d4943c718c95a75432b91735bb647a8baec26e7bc125adabbbe58f6f972",
"md5": "18d83535cc4a5b650c780cacf095c2f4",
"sha256": "d5076edc9d497cf6b0e6e6de95147e245a50f9e16cb6b229566adedd68a39b9d"
},
"downloads": -1,
"filename": "vault_exodus-0.1.3.7.tar.gz",
"has_sig": false,
"md5_digest": "18d83535cc4a5b650c780cacf095c2f4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 13608,
"upload_time": "2025-02-06T11:12:56",
"upload_time_iso_8601": "2025-02-06T11:12:56.960665Z",
"url": "https://files.pythonhosted.org/packages/14/17/3d4943c718c95a75432b91735bb647a8baec26e7bc125adabbbe58f6f972/vault_exodus-0.1.3.7.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-06 11:12:56",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "andylow92",
"github_project": "exodus",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "vault-exodus"
}