A self-hosted zero trust security platform for Proxmox environments, providing unified zero trust controls for network, identity, and workloads (Docker/LXC containers).
Disclaimer: This is a personal project and is not intended for use in production environments. It is provided as-is with no guarantees of stability, security, or support.
┌─────────────────────────────────────────────────────────────────────┐
│ Clients │
│ (Browser / CLI / API consumers) │
└──────────────┬──────────────────────────────────┬───────────────────┘
│ HTTPS (REST) │ gRPC (mTLS)
▼ ▼
┌──────────────────────────────────┐ ┌───────────────────────────────┐
│ zeroize-server │ │ Proxmox Agents │
│ (Control Plane) │ │ (one per host node) │
│ │ │ │
│ ┌────────────┐ ┌─────────────┐ │ │ ┌──────────┐ ┌──────────┐ │
│ │ REST API │ │ OIDC Auth │ │ │ │ Register │ │Heartbeat │ │
│ │ (axum) │ │ + JWT │ │ │ │ │ │+ Status │ │
│ └──────┬─────┘ └──────┬──────┘ │ │ └──────────┘ └──────────┘ │
│ │ │ │ └───────────────────────────────┘
│ ┌──────┴──────────────┴──────┐ │
│ │ AppState │ │ ┌─────────────────────┐
│ │ ┌──────────────────────┐ │ │ │ zeroize-proto │
│ │ │ SecretCipher │ │ │ │ (gRPC defs) │
│ │ │ (ChaCha20-Poly1305) │ │ │ │ │
│ │ ├──────────────────────┤ │ │ │ AgentService │
│ │ │ SessionManager │ │ │ │ ├ Register │
│ │ │ (CSRF state store) │ │ │ │ ├ Heartbeat │
│ │ ├──────────────────────┤ │ │ │ ├ GetConfig │
│ │ │ PolicyEngine │ │ │ │ └ ReportStatus │
│ │ │ (rule evaluation) │ │ │ └─────────────────────┘
│ │ ├──────────────────────┤ │ │
│ │ │ IdentityProvider │ │ │ ┌─────────────────────┐
│ │ │ (OIDC client) │ │ │ │ zeroize-common │
│ │ └──────────────────────┘ │ │ │ (shared types) │
│ └────────────┬───────────────┘ │ │ │
│ │ │ │ Policy, Secret, │
│ ┌────────────▼───────────────┐ │ │ User, Agent, │
│ │ Store (SQLite) │ │ │ Session, AuthState │
│ │ │ │ └─────────────────────┘
│ │ policies · secrets │ │
│ │ users · sessions │ │ ┌─────────────────────┐
│ │ agents · audit_log │ │ │ zeroize-cli │
│ └────────────────────────────┘ │ │ REST client │
└──────────────────────────────────┘ └─────────────────────┘
Create: Client ─► REST API ─► SecretCipher.encrypt() ─► Store (encrypted blob)
│
└─ random 12-byte nonce ║ ciphertext+tag
Read: Client ◄─ REST API ◄─ SecretCipher.decrypt() ◄─ Store (encrypted blob)
│
└─ audit_log entry written
1. GET /auth/login ─► Generate auth URL + CSRF state
Store (csrf_token, nonce) in SessionManager
Return redirect URL to OIDC provider
2. GET /auth/callback ─► Validate CSRF state
Exchange auth code for tokens (OIDC provider)
Verify ID token, extract claims
Upsert user in DB
Generate local JWT access + refresh tokens
Create DB session (hashed tokens)
Audit log
Return token pair
3. POST /auth/refresh ─► Validate refresh JWT
Look up session + user from DB
Issue new token pair
- Policy Engine: Flexible policy-based access control with support for network, access, workload, and secrets policies
- Identity Management: Full OIDC integration (Keycloak, Authentik, Okta, etc.) with JWT session management
- Secrets Management: ChaCha20-Poly1305 authenticated encryption with audit logging
- Agent Communication: gRPC protobuf service definitions for agent registration, heartbeat, config distribution, and status reporting
- Audit Logging: Comprehensive audit trail for all operations
- Rust 1.75+
- SQLite 3.x (included) or PostgreSQL 14+ (optional)
protoc(Protocol Buffers compiler) — for gRPC proto compilation
# Build all crates
cargo build --release
# Run the server
./target/release/zeroize-server --help
# Run the CLI
./target/release/zeroize --helpIf protoc is not installed, the zeroize-proto crate will still compile with a stub (no generated types). Install protoc for full gRPC support:
# Debian/Ubuntu
apt-get install protobuf-compiler
# macOS
brew install protobuf
# Or download from https://github.com/protocolbuffers/protobuf/releases# Copy and configure
cp config/example.yaml config.yaml
# Edit config.yaml with your settings
# Start the server
./target/release/zeroize-server -c config.yaml
# Or with environment variables
DATABASE_URL=sqlite:zeroize.db ./target/release/zeroize-server# Check server health
curl http://localhost:8080/api/v1/health
# Or using the CLI
./target/release/zeroize healthzeroize/
├── Cargo.toml # Workspace root
├── config/
│ └── example.yaml # Example configuration
├── crates/
│ ├── zeroize-common/ # Shared types (Policy, Secret, User, Agent, etc.)
│ │ └── src/
│ │ ├── identity.rs # User, Session, AuthState, TokenClaims
│ │ ├── policy.rs # Policy, PolicyRule, PolicyType
│ │ ├── secret.rs # Secret, SecretMetadata, SecretValue
│ │ └── workload.rs # Agent, AgentStatus
│ ├── zeroize-server/ # Control plane server
│ │ └── src/
│ │ ├── main.rs # Entrypoint — wires all components
│ │ ├── crypto.rs # ChaCha20-Poly1305 encrypt/decrypt (ring)
│ │ ├── config/ # YAML + env config loading
│ │ ├── api/ # Axum REST handlers
│ │ │ ├── auth.rs # OIDC login, callback, refresh, logout
│ │ │ ├── secrets.rs # CRUD with encrypted storage
│ │ │ ├── policies.rs # Policy CRUD + evaluation
│ │ │ ├── agents.rs # Agent registration + status
│ │ │ ├── middleware.rs # JWT auth middleware
│ │ │ └── error.rs # Unified error handling
│ │ ├── identity/ # OIDC provider, JWT tokens, session state
│ │ ├── policy/ # Policy engine + rule evaluator
│ │ └── store/ # SQLite database layer
│ │ └── sqlite.rs # All CRUD: policies, secrets, users,
│ │ # sessions, agents, audit_log
│ ├── zeroize-cli/ # Command-line interface
│ └── zeroize-proto/ # gRPC protobuf definitions
│ ├── proto/
│ │ └── agent.proto # AgentService (Register, Heartbeat,
│ │ # GetConfig, ReportStatus)
│ ├── build.rs # tonic_build proto compilation
│ └── src/lib.rs # Re-exports generated code
└── README.md
zeroize-cli ──────► zeroize-common
▲
zeroize-server ──────────┘
│
└──► zeroize-common
zeroize-proto (standalone — no workspace dependents yet)
GET /api/v1/health
GET /api/v1/auth/login # Initiate OIDC login → returns auth URL
GET /api/v1/auth/callback # OIDC callback → exchanges code, returns JWT pair
POST /api/v1/auth/refresh # Refresh tokens → validates session, returns new JWT pair
DELETE /api/v1/auth/logout # Logout → invalidates session
GET /api/v1/policies # List policies
POST /api/v1/policies # Create policy
GET /api/v1/policies/{id} # Get policy
PUT /api/v1/policies/{id} # Update policy
DELETE /api/v1/policies/{id} # Delete policy
POST /api/v1/policies/evaluate # Evaluate request against policies
GET /api/v1/secrets # List secrets (metadata only)
POST /api/v1/secrets # Create secret (encrypted at rest)
GET /api/v1/secrets/{id} # Get secret value (audited, decrypted)
DELETE /api/v1/secrets/{id} # Delete secret
GET /api/v1/agents # List agents
POST /api/v1/agents/register # Register agent
GET /api/v1/agents/{id} # Get agent details
GET /api/v1/agents/{id}/status # Get agent status
rpc Register(RegisterRequest) → RegisterResponse
rpc Heartbeat(HeartbeatRequest) → HeartbeatResponse
rpc GetConfig(GetConfigRequest) → GetConfigResponse
rpc ReportStatus(ReportStatusRequest) → ReportStatusResponse
$ zeroize init --server http://localhost:8080
ℹ Initializing Zeroize CLI configuration...
ℹ Checking connection to http://localhost:8080...
✓ Connected to server (version: 0.1.0)
✓ Configuration saved
ℹ Run 'zeroize policy list' to verify the connection$ zeroize health
✓ Server is healthy (version: 0.1.0)# List all policies
$ zeroize policy list
# Filter by type
$ zeroize policy list --type network
# Create a new policy
$ zeroize policy create --name prod-access --type access --description "Production access policy" --priority 10
✓ Created policy 'prod-access' (c3d4e5f6-a7b8-9012-cdef-123456789012)
# Get policy details
$ zeroize policy get c3d4e5f6-a7b8-9012-cdef-123456789012
# Enable / disable a policy
$ zeroize policy enable c3d4e5f6-a7b8-9012-cdef-123456789012
$ zeroize policy disable c3d4e5f6-a7b8-9012-cdef-123456789012
# Delete a policy
$ zeroize policy delete c3d4e5f6-a7b8-9012-cdef-123456789012# List all secrets (metadata only, values are never exposed in list)
$ zeroize secret list
# Create a secret (prompts for value if --value is omitted)
$ zeroize secret create --name api-token --path /apps/myapp/api --type api_key
# Create with inline value
$ zeroize secret create --name db-password --path /apps/myapp/db --type password --value "s3cur3_p@ss"
# Retrieve a secret (audited)
$ zeroize secret get d4e5f6a7-b8c9-0123-defa-234567890123
# Delete a secret
$ zeroize secret delete d4e5f6a7-b8c9-0123-defa-234567890123# List registered agents
$ zeroize agent list
# Register a new agent
$ zeroize agent register --name proxmox-node2 --host 192.168.1.11
# Get agent details
$ zeroize agent get e5f6a7b8-c9d0-1234-efab-345678901234
# Check agent status
$ zeroize agent status e5f6a7b8-c9d0-1234-efab-345678901234# Use a different server
$ zeroize --server http://10.0.0.5:8080 health
# Authenticate with a token
$ zeroize --token <your-token> policy list
# Output as JSON or YAML
$ zeroize --output json policy list
$ zeroize --output yaml secret list
# Enable debug logging
$ zeroize --debug healthSee config/example.yaml for all configuration options.
Key settings:
server.port: HTTP server port (default: 8080)database.url: Database connection stringauth.oidc: OIDC provider configuration (issuer, client_id, client_secret, redirect_uri)auth.jwt.secret: JWT signing secret (change in production!)secrets.encryption_key: 32-byte ChaCha20-Poly1305 key (change in production!)
Environment variable overrides:
ZEROIZE_JWT_SECRET— overridesauth.jwt.secretZEROIZE_ENCRYPTION_KEY— overridessecrets.encryption_keyDATABASE_URL— overridesdatabase.url(via CLI flag)
- Change default secrets: The default JWT secret and encryption key MUST be changed in production
- Encryption key: Must be exactly 32 bytes — the server will refuse to start otherwise
- Use TLS: Enable TLS in production environments
- OIDC: Configure OIDC for production authentication; the callback handler performs full token exchange and ID token verification
- Token storage: Session tokens are stored as SHA-256 hashes in the database
- Database: Consider PostgreSQL for production deployments
# Run tests
cargo test
# Run with debug logging
RUST_LOG=debug cargo run -p zeroize-server
# Check formatting
cargo fmt --check
# Run lints
cargo clippy
# Rebuild proto definitions (requires protoc)
PROTOC=$(which protoc) cargo build -p zeroize-protoApache-2.0