Python library for sealing and unsealing Kubernetes Sealed Secrets
Project description
kubesealpy
A Python library for sealing and unsealing Kubernetes secrets, compatible with Bitnami's Sealed Secrets.
Features
- Native Python implementation - No dependency on the
kubesealCLI - Full compatibility - Uses the same hybrid encryption (RSA-OAEP + AES-256-GCM) as kubeseal
- Multiple certificate sources - Load from files, URLs, or directly from a Kubernetes cluster
- All sealing scopes - Supports strict, namespace-wide, and cluster-wide scopes
- Multi-key decryption - Try multiple private keys for key rotation scenarios
- Type-safe - Full type hints and Pydantic models
- CLI - New CLI coming in 2006
Installation
pip install kubesealpy
For development:
pip install kubesealpy[dev]
Quick Start
Sealing a Secret
from kubesealpy import Sealer, SealingScope
# Create a sealer from a certificate file
sealer = Sealer.from_certificate_file("/path/to/cert.pem")
# Seal a secret
sealed = sealer.seal(
name="my-secret",
namespace="default",
data={
"username": b"admin",
"password": b"supersecret",
},
)
# Output as YAML (ready to apply to Kubernetes)
print(sealed.to_yaml())
Output:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: my-secret
namespace: default
spec:
encryptedData:
username: AgBz...
password: AgCx...
template:
metadata:
name: my-secret
namespace: default
type: Opaque
Sealing from a Kubernetes Cluster
from kubesealpy import Sealer, SealingScope
# Fetch the certificate directly from your cluster's sealed-secrets controller
sealer = Sealer.from_cluster(
controller_name="my-sealed-secrets",
controller_namespace="sealed-secrets",
)
# Seal a secret
sealed = sealer.seal(
name="my-secret",
namespace="default",
data={"api-key": b"secret-api-key"},
)
print(sealed.to_yaml())
Unsealing a Secret
from kubesealpy import Unsealer, SealedSecret
# Create an unsealer from a private key
unsealer = Unsealer.from_private_key_file("/path/to/private-key.pem")
# Load and unseal
sealed = SealedSecret.from_yaml(open("sealed-secret.yaml").read())
secret = unsealer.unseal(sealed)
# Access the decrypted data
import base64
password = base64.b64decode(secret.data["password"])
print(password.decode()) # "supersecret"
Certificate Sources
From a File
sealer = Sealer.from_certificate_file("/path/to/cert.pem")
From a URL
sealer = Sealer.from_certificate_url("https://example.com/cert.pem")
From a Kubernetes Cluster
# Uses your current kubeconfig context
sealer = Sealer.from_cluster()
# Or specify the controller location
sealer = Sealer.from_cluster(
controller_name="sealed-secrets-controller",
controller_namespace="kube-system",
)
From PEM Data
pem_data = open("cert.pem").read()
sealer = Sealer.from_certificate_pem(pem_data)
Sealing Scopes
Sealed Secrets supports three scoping modes that determine where a secret can be unsealed:
Strict (Default)
The secret is bound to both its name and namespace. It cannot be decrypted if either changes.
sealed = sealer.seal(
name="my-secret",
namespace="production",
data={"key": b"value"},
scope=SealingScope.STRICT,
)
Namespace-wide
The secret is bound only to its namespace. The name can change.
sealed = sealer.seal(
name="my-secret",
namespace="production",
data={"key": b"value"},
scope=SealingScope.NAMESPACE_WIDE,
)
Cluster-wide
The secret can be decrypted in any namespace with any name.
sealed = sealer.seal(
name="my-secret",
namespace="production",
data={"key": b"value"},
scope=SealingScope.CLUSTER_WIDE,
)
Working with Existing Secrets
You can seal an existing Kubernetes Secret object:
from kubesealpy import Secret, Sealer
# Create a Secret object
secret = Secret.from_string_data(
name="my-secret",
namespace="default",
data={
"username": "admin",
"password": "secret123",
},
secret_type="Opaque",
labels={"app": "myapp"},
)
# Seal it
sealer = Sealer.from_certificate_file("cert.pem")
sealed = sealer.seal_secret(secret)
Multi-Key Decryption
For key rotation scenarios, you can provide multiple private keys:
# From a directory of key files
unsealer = Unsealer.from_private_keys_directory("/path/to/keys/")
# The unsealer will try each key until one works
secret = unsealer.unseal(sealed_secret)
API Reference
Sealer
| Method | Description |
|---|---|
Sealer(public_key) |
Create from an RSA public key |
Sealer.from_certificate_file(path) |
Create from a PEM certificate file |
Sealer.from_certificate_url(url) |
Create by fetching a certificate from a URL |
Sealer.from_certificate_pem(data) |
Create from PEM certificate data |
Sealer.from_cluster() |
Create by fetching from a Kubernetes cluster |
seal(name, namespace, data, scope) |
Seal a secret |
seal_secret(secret, scope) |
Seal an existing Secret object |
seal_value(value, namespace, name, scope) |
Seal a single value |
Unsealer
| Method | Description |
|---|---|
Unsealer(private_keys) |
Create from a list of RSA private keys |
Unsealer.from_private_key(key) |
Create from a single private key |
Unsealer.from_private_key_file(path) |
Create from a PEM private key file |
Unsealer.from_private_keys_directory(dir) |
Create from all keys in a directory |
unseal(sealed_secret) |
Unseal a SealedSecret |
unseal_value(value, namespace, name, scope) |
Unseal a single value |
Models
| Class | Description |
|---|---|
Secret |
Kubernetes Secret resource |
SealedSecret |
Bitnami SealedSecret CRD |
SealingScope |
Enum: STRICT, NAMESPACE_WIDE, CLUSTER_WIDE |
Exceptions
| Exception | Description |
|---|---|
KubesealPyError |
Base exception for all kubesealpy errors |
EncryptionError |
Encryption operation failed |
DecryptionError |
Decryption operation failed |
CertificateError |
Certificate operation failed |
CertificateExpiredError |
Certificate has expired |
PrivateKeyError |
Private key operation failed |
NoMatchingKeyError |
No private key could decrypt the data |
Development
# Clone the repository
git clone https://gitlab.com/nighthawk-oss/kubesealpy
cd kubesealpy
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest -v
# Run tests with coverage
pytest --cov=kubesealpy --cov-report=term-missing
# Type checking
mypy src/kubesealpy
# Linting
ruff check src/kubesealpy
How It Works
kubesealpy implements the same hybrid encryption scheme as kubeseal:
- Session Key Generation: A random 32-byte AES key is generated
- RSA Encryption: The session key is encrypted using RSA-OAEP with SHA-256
- AES Encryption: The secret data is encrypted using AES-256-GCM with the session key
- Label Binding: A cryptographic label (based on scope) is included in RSA-OAEP to bind the ciphertext to specific namespace/name
The output format is:
[2-byte key length (big-endian)] | [RSA-encrypted session key] | [AES-GCM ciphertext]
License
MIT License
Related Projects
- Bitnami Sealed Secrets - The original project
- kubeseal - Official CLI tool
Author
Brandon Handeland < pypi at unbuffered dot net >
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kubesealpy-1.0.0.tar.gz.
File metadata
- Download URL: kubesealpy-1.0.0.tar.gz
- Upload date:
- Size: 14.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.12.12 Linux/5.15.154+
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a3257acabc8c0ca08658cfc3a9d802f48cfd09d9037613c136e98931258e610
|
|
| MD5 |
339197c85d0244ef6a1917455b765103
|
|
| BLAKE2b-256 |
52f188c12d0cdb800045ec05086e8000b7034ad7d3b3dc4a201b9a58ddc0a819
|
File details
Details for the file kubesealpy-1.0.0-py3-none-any.whl.
File metadata
- Download URL: kubesealpy-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.2.1 CPython/3.12.12 Linux/5.15.154+
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c677be19bb67d2fba658db5159db7e3dadcb4af808a14bc9148044528fcef1e6
|
|
| MD5 |
3600447d9b58e3fa1159c3025b8ce400
|
|
| BLAKE2b-256 |
39d8f029685898353ed47f66dbb9dda89d0dbeb6a6d38224ce78fe3a4b0b7ae5
|