Metadata-Version: 2.4
Name: helvetisafe-vault-k8s
Version: 0.1.3
Summary: Kubernetes integration for Helvetisafe secret management — sidecar agent, Secret sync, and init-container injection
Author: Helvetisafe contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/TheM0f/aegis
Project-URL: Documentation, https://github.com/TheM0f/aegis/tree/main/clients/kubernetes/README.md
Project-URL: Repository, https://github.com/TheM0f/aegis
Project-URL: Issue Tracker, https://github.com/TheM0f/aegis/issues
Project-URL: Changelog, https://github.com/TheM0f/aegis/releases
Keywords: secrets,vault,kubernetes,k8s,sidecar,zero-knowledge,helvetisafe
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: helvetisafe-client>=0.1.0
Provides-Extra: kubernetes
Requires-Dist: kubernetes>=28.0.0; extra == "kubernetes"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# helvetisafe-vault-k8s

Python package for consuming [Helvetisafe](https://vault.helvetisafe.ch) secrets in any environment — locally, in CI, or inside a Kubernetes workload. Built on the Helvetisafe Python client and its zero-knowledge encryption model.

Three CLI modes are available:

| Mode | Command | Description |
|------|---------|-------------|
| **Serve** | `helvetisafe-vault-k8s serve` | HTTP agent that exposes decrypted secrets on `127.0.0.1` |
| **Inject** | `helvetisafe-vault-k8s inject` | Write secrets to a single env file (`--output`) or one file per secret (`--output-dir`) |
| **Sync** | `helvetisafe-vault-k8s sync` | Sync secrets into a Kubernetes `Secret` object |

> Kubernetes deployment manifests and examples are maintained separately in the [aegis](https://github.com/helvetisafe/aegis) repository under `clients/kubernetes/`.

## Status

✅ v0.1.0 — available

---

## Installation

```bash
pip install helvetisafe-vault-k8s
```

The `sync` mode requires the `kubernetes` extra:

```bash
pip install "helvetisafe-vault-k8s[kubernetes]"
```

**Requirements:** Python 3.8+ · `helvetisafe-client >= 0.1.0` (auto-installed) · `kubernetes >= 28.0.0` (optional, `sync` only)

---

## Configuration

All settings are read from environment variables.

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `HELVETISAFE_BASE_URL` | ✅ | — | Base URL of the Helvetisafe instance, e.g. `https://vault.helvetisafe.ch` |
| `HELVETISAFE_CLIENT_ID` | ✅ | — | OAuth 2.0 client ID |
| `HELVETISAFE_CLIENT_SECRET` | ✅ | — | OAuth 2.0 client secret |
| `HELVETISAFE_PASSWORD` | one of three | — | Service account password |
| `HELVETISAFE_PRIVATE_KEY_PATH` | one of three | — | Path to a PEM private key file |
| `HELVETISAFE_PRIVATE_KEY_PEM` | one of three | — | PEM private key as a string |
| `HELVETISAFE_AGENT_PORT` | | `8080` | Port the serve agent listens on |
| `HELVETISAFE_AGENT_HOST` | | `127.0.0.1` | Host the serve agent binds to |
| `HELVETISAFE_K8S_NAMESPACE` | | `default` | Kubernetes namespace (sync mode) |
| `HELVETISAFE_K8S_SECRET_NAME` | | — | Kubernetes Secret name (sync mode) |
| `HELVETISAFE_SECRET_KEYS` | | — | Comma-separated keys to fetch; empty means all |

Exactly **one** of `HELVETISAFE_PASSWORD`, `HELVETISAFE_PRIVATE_KEY_PATH`, or `HELVETISAFE_PRIVATE_KEY_PEM` must be set.

---

## Modes

### Serve

Starts a lightweight HTTP server bound to `127.0.0.1` (loopback only). Any process on the same host can fetch decrypted secrets without implementing any cryptographic logic.

```bash
helvetisafe-vault-k8s serve [--port PORT] [--host HOST]
```

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/health` | Returns `{"status": "ok"}` |
| `GET` | `/secrets` | Lists all secret key names |
| `GET` | `/secret/{key}` | Returns the decrypted key/value pairs as a flat JSON object |

The response for `GET /secret/{key}` is always a flat JSON object. When the secret stores multiple key/value pairs (e.g. all database credentials under one secret path), every pair is returned at the top level. For a single plain-string secret the response is `{"<key>": "<value>"}`. The Helvetisafe secret path is never included in the response body.

```python
import requests

# Single-value secret → {"database_password": "s3cr3t"}
secret = requests.get("http://localhost:8080/secret/database_password").json()
value = secret["database_password"]

# Multi-value secret → {"DB_HOST": "localhost", "DB_PASS": "s3cr3t"}
db = requests.get("http://localhost:8080/secret/db_credentials").json()
host = db["DB_HOST"]
```

### Inject

Fetches secrets once and materialises them for consumption by the main application. Designed for the init-container pattern: the init-container runs this command to populate a shared `emptyDir` volume before the main application starts.

Exactly one output mode must be specified.

#### Option A — single env file (`--output`)

Writes all secrets to a single file, one `KEY=value` pair per line.

```bash
helvetisafe-vault-k8s inject --output /path/to/secrets.env
```

Output format (no quoting):

```
KEY_NAME=plaintext-value
ANOTHER_KEY=another-value
```

Literal newline characters inside values are replaced with the two-character sequence `\n` to keep each entry on a single line. No other escaping is applied.

#### Option B — one file per secret (`--output-dir`)

Writes one file per secret into a directory. The file name is the secret key and the file content is the raw plaintext value (no escaping). The directory is created if it does not exist.

```bash
helvetisafe-vault-k8s inject --output-dir /secrets/
```

Resulting layout:

```
/secrets/
├── KEY_NAME        # contains: plaintext-value
└── ANOTHER_KEY     # contains: another-value
```

This format is convenient for workloads that read individual secret files from a mounted volume (e.g. via `envFrom` with a `secretKeyRef`, or by reading `/secrets/<KEY>` directly).

> **Note:** Unlike `sync`, `inject` never creates or updates a Kubernetes `Secret`, even if `HELVETISAFE_K8S_SECRET_NAME` is set.

### Sync

Fetches secrets and creates or updates a Kubernetes `Secret` object.

```bash
helvetisafe-vault-k8s sync [--output /path/to/secrets.env]
```

Requires the `kubernetes` extra and a service account with `get`, `create`, and `update` permissions on `secrets` in the target namespace.

---

## Architecture

```
Client process
├── Your application
│       └─► GET http://127.0.0.1:8080/secret/db_password   (serve mode)
│
└── helvetisafe-vault-k8s
        ├─► POST https://vault.helvetisafe.ch/oauth/token
        │       ← access_token
        ├─► GET  /api/v1/credentials/org-key
        │       ← encrypted Org Key (RSA-OAEP)
        │   RSA decrypt → Org Key  (in memory only)
        └─► GET  /api/v1/secrets/db_password
                ← AES-256-GCM ciphertext
            AES decrypt → plaintext
```

Plaintext values never leave the process memory. The Helvetisafe server only ever stores and transmits ciphertext.

---

## Project Structure

```
helvetisafe-vault-k8s/
├── pyproject.toml
├── src/
│   └── helvetisafe_k8s/
│       ├── agent.py     # Serve mode HTTP server
│       ├── cli.py       # CLI entry point
│       ├── config.py    # Environment-variable config loader
│       └── sync.py      # Inject / sync utilities
└── tests/
    ├── test_agent.py
    ├── test_config.py
    └── test_sync.py
```

---

## Running Tests

```bash
pip install -e ".[dev]"
pytest tests/ -v
```

---

## Contributing

Contributions are welcome. Please open an issue or pull request in this repository.

## License

MIT
