secretumvault/docs/architecture/complete-architecture.md

1332 lines
36 KiB
Markdown
Raw Normal View History

2025-12-22 21:34:01 +00:00
# SecretumVault - Complete Post-Quantum Secrets Management System
**Version:** 3.0
**Date:** 2025-12-21
**Author:** Jesús Pérez (Kit Digital / Rust Las Palmas)
**Project Name:** `secretumvault` or `svault`
## Executive Summary
**SecretumVault** es un sistema completo de gestión de secretos con:
-**Post-Quantum Crypto** desde el diseño (no retrofitted)
-**Secrets Management** completo (KV, dynamic, transit)
-**Cedar Policies** (modern authorization, no ACL legacy)
-**Multi-Backend Crypto** (aws-lc-rs, RustCrypto, Tongsuo)
-**Encryption as a Service** (EaaS)
-**Storage Flexible** (SurrealDB, filesystem, etcd)
-**API Compatible** con HashiCorp Vault (migration path)
-**Rust Native** (memory safe, high performance)
**NO es**:
- ❌ NO tiene auth complejo (OIDC, LDAP) - solo lo esencial
- ❌ NO tiene ACL legacy - usa Cedar policies
- ❌ NO compite con Enterprise Vault features
**Es ideal para**:
- ✅ Kit Digital projects (PYMES españolas)
- ✅ Aplicaciones que necesitan PQC ahora
- ✅ Infraestructura moderna (Kubernetes, SurrealDB)
- ✅ Compliance NIS2 + post-quantum readiness
---
## Table of Contents
1. [Core Concepts](#core-concepts)
2. [Architecture](#architecture)
3. [Secrets Engines](#secrets-engines)
4. [Authorization with Cedar](#authorization-with-cedar)
5. [Crypto Backends](#crypto-backends)
6. [Storage Backends](#storage-backends)
7. [API Design](#api-design)
8. [Deployment Modes](#deployment-modes)
9. [Integration Examples](#integration-examples)
10. [Implementation Roadmap](#implementation-roadmap)
---
## Core Concepts
### What is SecretumVault?
```
SecretumVault = Secrets Manager + Encryption Service + Key Management
+ Cedar Policies + Post-Quantum Crypto
```
### Key Features
1. **Secrets Management**
- Key/Value secrets (static)
- Dynamic secrets (database, cloud)
- Transit encryption (EaaS)
- SSH/TLS certificate generation
2. **Post-Quantum Ready**
- ML-KEM for key encapsulation
- ML-DSA for signatures
- Hybrid modes (classical + PQC)
- Future-proof key rotation
3. **Modern Authorization**
- Cedar policy language (AWS open-source)
- Attribute-based access control (ABAC)
- No legacy ACL complexity
- Policy-as-Code
4. **High Performance**
- Rust native (no GC pauses)
- Async/await throughout
- Optimized crypto backends
- Efficient storage
---
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
├─────────────────────────────────────────────────────────────────┤
│ REST API │ CLI │ Rust SDK │ Language SDKs (future) │
└──────┬──────────────────────────────────────────────────────────┘
┌──────▼──────────────────────────────────────────────────────────┐
│ QUANTUMVAULT CORE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Request │ │ Auth/Policy │ │ Secrets Router │ │
│ │ Handler │→ │ Engine │→ │ (Path-based) │ │
│ └─────────────┘ └──────────────┘ └──────────────────────┘ │
│ │ │ │
│ ↓ ↓ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ Cedar Policies │ │ Secrets Engines │ │
│ │ (Authorization) │ │ (KV, Transit, │ │
│ └──────────────────┘ │ Dynamic, PKI) │ │
│ └────────────────────┘ │
│ │ │
└──────────────────────────────────────────────┼──────────────────┘
┌───────────────────────────────────────┼───────────────┐
│ │ │
┌──────▼────────┐ ┌──────────────▼──────┐ ┌▼──────────────┐ │
│ Crypto Layer │ │ Storage Layer │ │ Seal/Unseal │ │
├───────────────┤ ├─────────────────────┤ ├───────────────┤ │
│ • aws-lc-rs │ │ • SurrealDB │ │ • Master Key │ │
│ • RustCrypto │ │ • Filesystem │ │ • Shamir SSS │ │
│ • Tongsuo │ │ • etcd/Consul │ │ • Auto-unseal │ │
│ • OpenSSL │ │ • PostgreSQL │ │ (KMS) │ │
└───────────────┘ └─────────────────────┘ └───────────────┘ │
└────────────────────────────────────────────────────────────────┘
```
---
## Secrets Engines
SecretumVault soporta múltiples "engines" montados en paths:
### 1. KV Engine (Key-Value Secrets)
**Path**: `secret/`
```rust
// API
POST /v1/secret/data/myapp/database # Write secret
GET /v1/secret/data/myapp/database # Read secret
DELETE /v1/secret/data/myapp/database # Delete secret
GET /v1/secret/metadata/myapp/database # Get metadata
```
**Implementación**:
```rust
pub struct KVEngine {
storage: Arc<dyn StorageBackend>,
versioned: bool, // KV v1 o v2
}
#[derive(Serialize, Deserialize)]
pub struct SecretData {
pub data: HashMap<String, serde_json::Value>,
pub metadata: SecretMetadata,
}
#[derive(Serialize, Deserialize)]
pub struct SecretMetadata {
pub version: u64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub deleted: bool,
pub destroyed: bool,
}
impl SecretsEngine for KVEngine {
async fn write(
&self,
path: &str,
data: HashMap<String, serde_json::Value>,
) -> Result<SecretMetadata> {
let mut metadata = SecretMetadata {
version: self.get_next_version(path).await?,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted: false,
destroyed: false,
};
let secret = SecretData { data, metadata: metadata.clone() };
// Encrypt data before storing
let encrypted = self.encrypt_secret(&secret).await?;
self.storage.store_secret(path, &encrypted).await?;
Ok(metadata)
}
async fn read(&self, path: &str) -> Result<SecretData> {
let encrypted = self.storage.get_secret(path).await?;
let secret = self.decrypt_secret(&encrypted).await?;
if secret.metadata.deleted {
return Err(Error::SecretDeleted);
}
Ok(secret)
}
}
```
### 2. Transit Engine (Encryption as a Service)
**Path**: `transit/`
Permite a aplicaciones cifrar/descifrar datos sin manejar claves directamente.
```rust
// API
POST /v1/transit/encrypt/my-key # Encrypt data
POST /v1/transit/decrypt/my-key # Decrypt data
POST /v1/transit/sign/my-key # Sign data (PQC)
POST /v1/transit/verify/my-key # Verify signature
POST /v1/transit/rewrap/my-key # Re-encrypt with new key version
```
**Implementación**:
```rust
pub struct TransitEngine {
crypto: Arc<dyn CryptoBackend>,
key_manager: Arc<KeyManager>,
}
#[derive(Deserialize)]
pub struct EncryptRequest {
pub plaintext: String, // Base64
pub context: Option<String>, // Additional authenticated data
}
#[derive(Serialize)]
pub struct EncryptResponse {
pub ciphertext: String, // vault:v1:base64ciphertext
}
impl SecretsEngine for TransitEngine {
async fn handle_encrypt(
&self,
key_name: &str,
req: EncryptRequest,
) -> Result<EncryptResponse> {
// 1. Decode plaintext
let plaintext = base64::decode(&req.plaintext)?;
// 2. Get encryption key (latest version)
let key = self.key_manager.get_key(key_name).await?;
// 3. Encrypt with crypto backend
let ciphertext = match key.algorithm {
KeyAlgorithm::Aes256Gcm => {
self.crypto.encrypt_symmetric(&key.key_data, &plaintext, SymmetricAlgorithm::Aes256Gcm)?
}
KeyAlgorithm::ChaCha20Poly1305 => {
self.crypto.encrypt_symmetric(&key.key_data, &plaintext, SymmetricAlgorithm::ChaCha20Poly1305)?
}
_ => return Err(Error::InvalidAlgorithm),
};
// 4. Format response (Vault-compatible)
let encoded = format!(
"vault:v{}:{}",
key.version,
base64::encode(&ciphertext)
);
Ok(EncryptResponse {
ciphertext: encoded,
})
}
async fn handle_sign(
&self,
key_name: &str,
data: &[u8],
) -> Result<String> {
let key = self.key_manager.get_key(key_name).await?;
// Firmar con PQC (ML-DSA)
let signature = self.crypto.sign(&key.private_key, data)?;
Ok(format!(
"vault:v{}:{}",
key.version,
base64::encode(&signature)
))
}
}
```
### 3. PKI Engine (Certificate Authority)
**Path**: `pki/`
Genera certificados X.509 (RSA/ECDSA tradicional + PQC experimental).
```rust
pub struct PKIEngine {
crypto: Arc<dyn CryptoBackend>,
ca_bundle: Option<CABundle>,
}
#[derive(Deserialize)]
pub struct IssueCertRequest {
pub common_name: String,
pub alt_names: Vec<String>,
pub ttl: String, // e.g., "720h"
pub algorithm: CertAlgorithm, // RSA2048, ECDSA256, ML-DSA-65
}
impl SecretsEngine for PKIEngine {
async fn issue_cert(&self, req: IssueCertRequest) -> Result<Certificate> {
// 1. Generate keypair
let keypair = self.crypto.generate_keypair(
req.algorithm.to_key_algorithm()
)?;
// 2. Create CSR
let csr = create_csr(&keypair, &req.common_name, &req.alt_names)?;
// 3. Sign with CA
let cert = self.ca_bundle
.as_ref()
.ok_or(Error::NoCAConfigured)?
.sign_csr(&csr, parse_duration(&req.ttl)?)?;
Ok(cert)
}
}
```
### 4. Dynamic Secrets Engine
**Path**: `database/`, `aws/`, `gcp/`
Genera credenciales dinámicas on-demand.
```rust
pub struct DatabaseEngine {
connections: HashMap<String, DatabaseConnection>,
}
#[derive(Deserialize)]
pub struct GetCredsRequest {
pub role: String, // e.g., "readonly", "admin"
}
#[derive(Serialize)]
pub struct DatabaseCreds {
pub username: String,
pub password: String,
pub lease_duration: u64, // seconds
}
impl SecretsEngine for DatabaseEngine {
async fn get_credentials(
&self,
role: &str,
) -> Result<DatabaseCreds> {
let role_config = self.get_role_config(role).await?;
// Generate random username/password
let username = format!("v-{}-{}", role, generate_id());
let password = generate_secure_password(32);
// Create user in database
let db = self.connections.get(&role_config.connection)
.ok_or(Error::ConnectionNotFound)?;
db.execute(&format!(
"CREATE USER '{}' IDENTIFIED BY '{}';",
username, password
)).await?;
// Grant permissions
for stmt in &role_config.creation_statements {
db.execute(stmt).await?;
}
// Schedule revocation
self.schedule_revocation(&username, role_config.ttl).await?;
Ok(DatabaseCreds {
username,
password,
lease_duration: role_config.ttl.as_secs(),
})
}
}
```
---
## Authorization with Cedar
En lugar de ACL tradicional, usamos **Cedar** (AWS open-source policy language).
### Cedar Overview
Cedar es:
- ✅ Expresivo y declarativo
- ✅ Verificable formalmente
- ✅ Policy-as-Code
- ✅ Usado por AWS (AVP)
### Policy Example
**Archivo**: `policies/allow-read-secrets.cedar`
```cedar
// Permitir a developers leer secrets de su proyecto
permit(
principal in Group::"developers",
action == Action::"read",
resource in Project::"kit-digital"
)
when {
context.environment == "development" ||
context.environment == "staging"
};
// Permitir a CI/CD leer solo secrets de deployment
permit(
principal == ServiceAccount::"github-actions",
action in [Action::"read", Action::"encrypt"],
resource in Path::"secret/deployments/*"
)
when {
context.ip_address in [
"192.30.252.0/22", // GitHub Actions IPs
"185.199.108.0/22"
]
};
// Permitir a admins todo
permit(
principal in Group::"admins",
action,
resource
);
// Denegar escritura a production desde dev environments
forbid(
principal,
action in [Action::"write", Action::"delete"],
resource in Path::"secret/production/*"
)
when {
context.environment != "production"
};
```
### Cedar Integration
**Archivo**: `src/auth/cedar.rs`
```rust
use cedar_policy::{Authorizer, Context, Entities, PolicySet, Request};
pub struct CedarAuthz {
authorizer: Authorizer,
policies: PolicySet,
entities: Entities,
}
impl CedarAuthz {
pub fn from_files(policy_dir: &str) -> Result<Self> {
let policies = load_policies_from_dir(policy_dir)?;
let entities = load_entities()?; // Groups, users, etc.
Ok(Self {
authorizer: Authorizer::new(),
policies,
entities,
})
}
pub fn is_authorized(
&self,
principal: &str,
action: &str,
resource: &str,
context: RequestContext,
) -> Result<bool> {
let request = Request::new(
Some(principal.parse()?),
Some(action.parse()?),
Some(resource.parse()?),
Context::from_json_value(serde_json::to_value(context)?)?,
)?;
let response = self.authorizer.is_authorized(
&request,
&self.policies,
&self.entities,
);
Ok(response.decision() == cedar_policy::Decision::Allow)
}
}
#[derive(Serialize)]
pub struct RequestContext {
pub environment: String,
pub ip_address: String,
pub timestamp: i64,
pub mfa_verified: bool,
}
// Integración con Vault
impl Vault {
async fn handle_request(
&self,
req: VaultRequest,
auth_token: &str,
) -> Result<VaultResponse> {
// 1. Validate token
let principal = self.auth.validate_token(auth_token).await?;
// 2. Build context
let context = RequestContext {
environment: req.headers.get("X-Vault-Environment")
.unwrap_or("unknown").to_string(),
ip_address: req.remote_addr.to_string(),
timestamp: Utc::now().timestamp(),
mfa_verified: self.auth.is_mfa_verified(&principal).await?,
};
// 3. Authorize with Cedar
let authorized = self.cedar.is_authorized(
&principal.id,
&req.operation, // "read", "write", "delete"
&req.path, // "secret/production/db"
context,
)?;
if !authorized {
return Err(Error::PermissionDenied);
}
// 4. Route to appropriate engine
self.route_request(req).await
}
}
```
### Entities Definition
**Archivo**: `entities.json`
```json
[
{
"uid": {"type": "User", "id": "alice"},
"attrs": {
"email": "alice@example.com",
"groups": ["developers", "admins"]
},
"parents": [
{"type": "Group", "id": "developers"},
{"type": "Group", "id": "admins"}
]
},
{
"uid": {"type": "ServiceAccount", "id": "github-actions"},
"attrs": {
"type": "ci-cd",
"trusted": true
}
},
{
"uid": {"type": "Group", "id": "developers"},
"attrs": {},
"parents": []
}
]
```
---
## Crypto Backends
### Backend Interface (ya definido anteriormente)
```rust
pub trait CryptoBackend: Debug + Send + Sync {
fn backend_type(&self) -> BackendType;
// Asymmetric
fn generate_keypair(&self, algorithm: KeyAlgorithm) -> Result<KeyPair>;
fn sign(&self, key: &PrivateKey, data: &[u8]) -> Result<Vec<u8>>;
fn verify(&self, key: &PublicKey, data: &[u8], sig: &[u8]) -> Result<bool>;
// Symmetric
fn encrypt_symmetric(&self, key: &[u8], data: &[u8], alg: SymmetricAlgorithm) -> Result<Vec<u8>>;
fn decrypt_symmetric(&self, key: &[u8], data: &[u8], alg: SymmetricAlgorithm) -> Result<Vec<u8>>;
// KEM (for PQC)
fn kem_encapsulate(&self, key: &PublicKey) -> Result<(Vec<u8>, Vec<u8>)>;
fn kem_decapsulate(&self, key: &PrivateKey, ct: &[u8]) -> Result<Vec<u8>>;
// Random
fn random_bytes(&self, len: usize) -> Result<Vec<u8>>;
}
```
### Hybrid Crypto Mode
Para defense-in-depth, soportar hybrid:
```rust
pub struct HybridBackend {
classical: Arc<dyn CryptoBackend>, // OpenSSL/aws-lc (RSA/ECDSA)
pqc: Arc<dyn CryptoBackend>, // aws-lc-rs (ML-KEM/ML-DSA)
}
impl CryptoBackend for HybridBackend {
fn kem_encapsulate(&self, key: &PublicKey) -> Result<(Vec<u8>, Vec<u8>)> {
// Encapsulate with both
let (ct_classical, ss_classical) = self.classical.kem_encapsulate(key)?;
let (ct_pqc, ss_pqc) = self.pqc.kem_encapsulate(key)?;
// Combine ciphertexts
let combined_ct = [ct_classical, ct_pqc].concat();
// Derive shared secret from both using KDF
let combined_ss = kdf_combine(&ss_classical, &ss_pqc)?;
Ok((combined_ct, combined_ss))
}
}
```
---
## Storage Backends
### Storage Trait
```rust
#[async_trait]
pub trait StorageBackend: Debug + Send + Sync {
// Secrets
async fn store_secret(&self, path: &str, data: &EncryptedData) -> Result<()>;
async fn get_secret(&self, path: &str) -> Result<EncryptedData>;
async fn delete_secret(&self, path: &str) -> Result<()>;
async fn list_secrets(&self, prefix: &str) -> Result<Vec<String>>;
// Keys
async fn store_key(&self, key: &StoredKey) -> Result<()>;
async fn get_key(&self, key_id: &str) -> Result<StoredKey>;
// Policies
async fn store_policy(&self, name: &str, policy: &str) -> Result<()>;
async fn get_policy(&self, name: &str) -> Result<String>;
async fn list_policies(&self) -> Result<Vec<String>>;
// Leases (for dynamic secrets)
async fn store_lease(&self, lease: &Lease) -> Result<()>;
async fn get_lease(&self, lease_id: &str) -> Result<Lease>;
async fn delete_lease(&self, lease_id: &str) -> Result<()>;
async fn list_expiring_leases(&self, before: DateTime<Utc>) -> Result<Vec<Lease>>;
}
```
### SurrealDB Implementation
```rust
pub struct SurrealDBBackend {
db: Surreal<Client>,
}
#[async_trait]
impl StorageBackend for SurrealDBBackend {
async fn store_secret(&self, path: &str, data: &EncryptedData) -> Result<()> {
let record = SecretRecord {
path: path.to_string(),
data: data.clone(),
created_at: Utc::now(),
updated_at: Utc::now(),
};
let _: Option<SecretRecord> = self.db
.create(("secrets", path))
.content(record)
.await?;
Ok(())
}
async fn list_expiring_leases(&self, before: DateTime<Utc>) -> Result<Vec<Lease>> {
let leases: Vec<Lease> = self.db
.query("SELECT * FROM leases WHERE expires_at <= $before")
.bind(("before", before))
.await?
.take(0)?;
Ok(leases)
}
}
```
---
## API Design
### Vault-Compatible REST API
```rust
// Core paths
GET /v1/sys/health // Health check
POST /v1/sys/init // Initialize vault
POST /v1/sys/unseal // Unseal vault
POST /v1/sys/seal // Seal vault
GET /v1/sys/seal-status // Get seal status
// Auth
POST /v1/auth/token/create // Create token
POST /v1/auth/token/revoke // Revoke token
POST /v1/auth/token/lookup // Lookup token
// Secrets (KV)
GET /v1/secret/data/:path // Read secret
POST /v1/secret/data/:path // Write secret
DELETE /v1/secret/data/:path // Delete secret
GET /v1/secret/metadata/:path // Get metadata
LIST /v1/secret/metadata/:path // List secrets
// Transit (EaaS)
POST /v1/transit/encrypt/:key // Encrypt data
POST /v1/transit/decrypt/:key // Decrypt data
POST /v1/transit/sign/:key // Sign data (PQC)
POST /v1/transit/verify/:key // Verify signature
POST /v1/transit/keys/:key // Create key
POST /v1/transit/keys/:key/rotate // Rotate key
// PKI
POST /v1/pki/root/generate // Generate root CA
POST /v1/pki/issue/:role // Issue certificate
POST /v1/pki/revoke // Revoke certificate
// Database (Dynamic)
GET /v1/database/creds/:role // Get dynamic creds
POST /v1/database/config/:name // Configure connection
// Policies (Cedar)
GET /v1/sys/policies // List policies
GET /v1/sys/policy/:name // Read policy
PUT /v1/sys/policy/:name // Write policy
DELETE /v1/sys/policy/:name // Delete policy
```
### Request/Response Format
```rust
#[derive(Serialize, Deserialize)]
pub struct VaultRequest {
pub path: String,
pub operation: Operation, // Read, Write, Delete, List
pub data: Option<serde_json::Value>,
pub headers: HashMap<String, String>,
}
#[derive(Serialize, Deserialize)]
pub struct VaultResponse {
pub request_id: String,
pub lease_id: Option<String>,
pub renewable: bool,
pub lease_duration: u64,
pub data: Option<serde_json::Value>,
pub warnings: Vec<String>,
pub auth: Option<AuthInfo>,
}
```
---
## Deployment Modes
### 1. Standalone Server
```bash
# Start server
svault server --config svault.toml
# Initialize
svault operator init
# Unseal
svault operator unseal <key>
```
### 2. Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: secretumvault
spec:
serviceName: secretumvault
replicas: 3
selector:
matchLabels:
app: secretumvault
template:
metadata:
labels:
app: secretumvault
spec:
containers:
- name: vault
image: secretumvault:latest
ports:
- containerPort: 8200
name: api
env:
- name: QVAULT_BACKEND
value: "aws-lc"
- name: QVAULT_STORAGE
value: "surrealdb"
volumeMounts:
- name: config
mountPath: /etc/secretumvault
- name: data
mountPath: /var/secretumvault
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
```
### 3. Library Mode (Embedded)
```rust
// En tu aplicación
use secretumvault::{Vault, VaultConfig};
#[tokio::main]
async fn main() -> Result<()> {
let config = VaultConfig::builder()
.crypto_backend(BackendType::AwsLc)
.storage_backend(StorageType::SurrealDB)
.build()?;
let vault = Vault::new(config).await?;
// Usar vault embebido
vault.write("secret/myapp/db", json!({
"username": "admin",
"password": "secret123"
})).await?;
Ok(())
}
```
---
## Integration Examples
### 1. Con RustyVault (como Backend)
SecretumVault puede servir como crypto backend de RustyVault:
```rust
// RustyVault usa SecretumVault para crypto
use secretumvault::CryptoService;
pub struct RustyVaultWithPQC {
secrets: RustyVaultCore,
crypto: Arc<CryptoService>, // SecretumVault crypto
}
impl RustyVaultWithPQC {
async fn sign_certificate(&self, csr: &[u8]) -> Result<Vec<u8>> {
// Usar SecretumVault para firmar con ML-DSA
self.crypto.sign("ca-key", csr).await
}
}
```
### 2. Con provctl (Orchestration)
```rust
use secretumvault::Vault;
async fn provision_secure_machine(machine_id: &str) -> Result<()> {
let vault = Vault::connect("https://vault.internal:8200").await?;
// Get dynamic SSH credentials
let ssh_creds = vault.read(&format!("ssh/creds/{}", machine_id)).await?;
// Encrypt machine config
let encrypted_config = vault.transit_encrypt(
"machine-config-key",
&machine_config.as_bytes()
).await?;
// Deploy
deploy_machine(machine_id, &ssh_creds, &encrypted_config).await?;
Ok(())
}
```
### 3. Con Nushell Scripts
```nu
# vault-ops.nu
export def get-db-creds [role: string] {
let response = (
http get http://vault:8200/v1/database/creds/$role
-H [X-Vault-Token $env.VAULT_TOKEN]
)
{
username: $response.data.username,
password: $response.data.password,
expires: ($response.lease_duration | into datetime)
}
}
export def encrypt-file [file: string, key: string] {
let plaintext = (open $file | encode base64)
let response = (
http post http://vault:8200/v1/transit/encrypt/$key {
plaintext: $plaintext
} -H [X-Vault-Token $env.VAULT_TOKEN]
)
$response.data.ciphertext | save $"($file).encrypted"
}
# Uso
get-db-creds "readonly" | save db-creds.json
encrypt-file "sensitive.txt" "app-key"
```
### 4. Con Nickel Config
```nickel
# vault-bootstrap.ncl
let Vault = {
server = "https://vault.internal:8200",
policies = {
developers = m%"
permit(
principal in Group::"developers",
action == Action::"read",
resource in Path::"secret/dev/*"
);
"%,
ci_cd = m%"
permit(
principal == ServiceAccount::"github-actions",
action in [Action::"read", Action::"encrypt"],
resource in Path::"secret/deployments/*"
);
"%,
},
secrets = {
database = {
connection = "postgresql://vault-db:5432/app",
roles = {
readonly = {
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
"GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
],
ttl = "1h"
},
admin = {
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' SUPERUSER;"
],
ttl = "15m"
}
}
}
}
}
{
vault = Vault
}
```
---
## Implementation Roadmap
### Phase 1: Foundation (Week 1-3)
**Week 1**: Core Architecture
- [ ] Project structure
- [ ] Storage trait + filesystem implementation
- [ ] Basic crypto backend (OpenSSL)
- [ ] Seal/Unseal mechanism with Shamir
**Week 2**: KV Engine
- [ ] KV secrets engine (v2 with versioning)
- [ ] Encryption at rest
- [ ] API handlers for KV
- [ ] CLI: read/write/delete
**Week 3**: Auth & Cedar
- [ ] Token-based authentication
- [ ] Cedar policy integration
- [ ] Policy evaluation
- [ ] Basic ACL tests
### Phase 2: Crypto Engines (Week 4-6)
**Week 4**: Transit Engine
- [ ] Encrypt/decrypt endpoints
- [ ] Key rotation
- [ ] Multiple algorithm support
- [ ] Rewrap functionality
**Week 5**: PQC Integration
- [ ] aws-lc-rs backend
- [ ] ML-KEM implementation
- [ ] ML-DSA signatures
- [ ] Hybrid mode (classical + PQC)
**Week 6**: PKI Engine
- [ ] Root CA generation
- [ ] Certificate issuance (RSA/ECDSA)
- [ ] CRL/OCSP support
- [ ] PQC certificates (experimental)
### Phase 3: Dynamic Secrets (Week 7-8)
**Week 7**: Database Engine
- [ ] PostgreSQL support
- [ ] MySQL support
- [ ] Lease management
- [ ] Automatic revocation
**Week 8**: Storage Backends
- [ ] SurrealDB implementation
- [ ] etcd/Consul support
- [ ] Migration tools
### Phase 4: Production Ready (Week 9-10)
**Week 9**: High Availability
- [ ] Leader election
- [ ] Replication
- [ ] Auto-unseal with KMS
- [ ] Performance optimizations
**Week 10**: Polish & Docs
- [ ] API documentation (OpenAPI)
- [ ] Deployment guides
- [ ] Security audit
- [ ] Benchmarks
---
## Configuration Example
**Archivo**: `svault.toml`
```toml
[vault]
# Crypto backend
crypto_backend = "aws-lc" # aws-lc | rustcrypto | tongsuo | openssl
# Storage backend
storage_backend = "surrealdb" # surrealdb | filesystem | etcd | consul
[server]
address = "0.0.0.0:8200"
tls_cert = "/etc/svault/tls/cert.pem"
tls_key = "/etc/svault/tls/key.pem"
# Mutual TLS (opcional)
tls_client_ca = "/etc/svault/tls/ca.pem"
[storage.surrealdb]
endpoint = "ws://localhost:8000"
namespace = "vault"
database = "production"
username = "vault"
password = "${SURREAL_PASSWORD}"
[storage.filesystem]
path = "/var/svault/data"
[seal]
type = "shamir" # shamir | auto | transit
shares = 5
threshold = 3
# Auto-unseal con KMS (opcional)
[seal.auto]
type = "aws-kms" # aws-kms | gcp-kms | azure-kv
key_id = "arn:aws:kms:..."
[auth.cedar]
policies_dir = "/etc/svault/policies"
entities_file = "/etc/svault/entities.json"
[engines]
# Secrets engines montados
kv = { path = "secret/", versioned = true }
transit = { path = "transit/" }
pki = { path = "pki/" }
database = { path = "database/" }
[logging]
level = "info"
format = "json"
output = "/var/log/svault/vault.log"
[telemetry]
prometheus_port = 9090
```
---
## Project Structure
```
secretumvault/
├── Cargo.toml
├── README.md
├── config/
│ └── svault.toml.example
2025-12-22 21:34:01 +00:00
├── src/
│ ├── lib.rs
│ ├── main.rs # Server binary
│ │
│ ├── core/
│ │ ├── mod.rs
│ │ ├── vault.rs # Main Vault struct
│ │ ├── seal.rs # Seal/Unseal logic
│ │ └── router.rs # Request routing
│ │
│ ├── engines/
│ │ ├── mod.rs
│ │ ├── kv.rs # KV engine
│ │ ├── transit.rs # Transit engine
│ │ ├── pki.rs # PKI engine
│ │ └── database.rs # Dynamic secrets
│ │
│ ├── auth/
│ │ ├── mod.rs
│ │ ├── token.rs # Token auth
│ │ └── cedar.rs # Cedar integration
│ │
│ ├── crypto/
│ │ ├── mod.rs
│ │ ├── backend.rs # Trait
│ │ ├── aws_lc.rs
│ │ ├── rustcrypto.rs
│ │ └── tongsuo.rs
│ │
│ ├── storage/
│ │ ├── mod.rs
│ │ ├── filesystem.rs
│ │ ├── surrealdb.rs
│ │ ├── etcd.rs
│ │ └── consul.rs
│ │
│ ├── api/
│ │ ├── mod.rs
│ │ ├── server.rs
│ │ └── handlers/
│ │ ├── sys.rs
│ │ ├── secret.rs
│ │ ├── transit.rs
│ │ └── pki.rs
│ │
│ ├── shamir/
│ │ └── mod.rs
│ │
│ └── error.rs
├── bin/
│ └── svault.rs # CLI tool
├── policies/
│ ├── default.cedar
│ ├── developers.cedar
│ └── ci-cd.cedar
├── scripts/
│ ├── vault-ops.nu # Nushell helpers
│ └── init-vault.sh
├── configs/
│ ├── vault-config.ncl
│ └── bootstrap.ncl
└── tests/
├── integration/
├── engines/
└── auth/
```
---
## Cargo.toml
```toml
[package]
name = "secretumvault"
version = "0.1.0"
edition = "2021"
authors = ["Jesús Pérez <contact@jesusperez.pro>"]
description = "Post-quantum ready secrets management system"
license = "Apache-2.0"
[features]
default = ["aws-lc", "filesystem", "server"]
# Crypto backends
aws-lc = ["aws-lc-rs"]
rustcrypto = ["ml-kem", "ml-dsa", "slh-dsa"]
tongsuo = ["dep:tongsuo"]
openssl = ["dep:openssl"]
# Storage backends
filesystem = []
surrealdb-storage = ["surrealdb"]
etcd-storage = ["etcd-client"]
consul-storage = ["consulrs"]
# Components
server = ["axum", "tower-http"]
cli = ["clap"]
cedar = ["cedar-policy"]
[dependencies]
# Core
tokio = { version = "1.35", features = ["full"] }
async-trait = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
thiserror = "1.0"
anyhow = "1.0"
chrono = { version = "0.4", features = ["serde"] }
tracing = "0.1"
tracing-subscriber = "0.3"
# Crypto
aws-lc-rs = { version = "1.9", features = ["unstable"], optional = true }
ml-kem = { version = "0.2", optional = true }
ml-dsa = { version = "0.2", optional = true }
slh-dsa = { version = "0.2", optional = true }
openssl = { version = "0.10", optional = true }
tongsuo = { version = "0.3", optional = true }
# Shamir Secret Sharing
sharks = "0.5"
# Cedar policies
cedar-policy = { version = "3.0", optional = true }
# Storage
surrealdb = { version = "1.5", optional = true }
etcd-client = { version = "0.13", optional = true }
consulrs = { version = "0.1", optional = true }
# Server
axum = { version = "0.7", optional = true, features = ["macros"] }
tower-http = { version = "0.5", optional = true, features = ["cors", "trace"] }
# CLI
clap = { version = "4.5", optional = true, features = ["derive"] }
# Utilities
uuid = { version = "1.6", features = ["v4", "serde"] }
base64 = "0.21"
hex = "0.4"
[dev-dependencies]
tempfile = "3.8"
wiremock = "0.5"
[[bin]]
name = "svault"
path = "src/main.rs"
[[bin]]
name = "svault-cli"
path = "bin/svault.rs"
required-features = ["cli"]
```
---
## ¿Por qué SecretumVault vs. Solo Crypto Service?
| Feature | Crypto Service Solo | SecretumVault Completo |
|---|---|---|
| Secrets Management | ❌ | ✅ KV + Dynamic |
| Encryption as a Service | ⚠️ Básico | ✅ Transit engine completo |
| Authorization | ❌ | ✅ Cedar policies |
| PKI/Certificates | ❌ | ✅ Full CA |
| Dynamic Secrets | ❌ | ✅ Database, cloud |
| Lease Management | ❌ | ✅ Auto-revocation |
| Audit Logging | ❌ | ✅ Completo |
| High Availability | ❌ | ✅ Replication |
| **Vault API Compatible** | ❌ | ✅ Migration path |
---
## Next Steps
1. **Validar arquitectura** - ¿Tiene sentido este scope?
2. **Naming** - ¿`secretumvault`, `svault`, otro?
3. **Prioridades** - ¿Qué implementar primero?
4. **Cedar policies** - ¿Es la mejor opción o prefieres ACL simple?
5. **Integración RustyVault** - ¿Backend? ¿Fork? ¿Proyecto separado?
**¿Empezamos con Phase 1 en Claude Code?**