Quick Start
Get Kluis running in 5 minutes with Docker.
1. Clone the Repository
git clone https://github.com/your-org/kluis.git
cd kluis 2. Configure Environment
Copy the example environment file and update with your settings:
cp .env.example .env Required environment variables:
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/kluis"
# Vault Connection
VAULT_ADDR="http://localhost:8200"
VAULT_TOKEN="your-root-token"
# JWT Secret (generate a secure random string)
JWT_SECRET="your-secure-jwt-secret"
# Session
SESSION_SECRET="your-session-secret" 3. Start with Docker Compose
docker-compose up -d This starts:
- PostgreSQL database
- Kluis backend (Express.js)
- Kluis frontend (React)
4. Access Kluis
Open http://localhost:3000 in your browser. The first user to register becomes SUPERADMIN.
Prerequisites
Required
- Node.js 20+ (for local development)
- PostgreSQL 15+
- OpenBao or HashiCorp Vault (with admin token)
Optional
- Docker & Docker Compose (recommended for deployment)
- OAuth Provider Credentials (Google, GitHub, Azure AD)
Vault Requirements
Kluis requires a Vault token with permissions to:
- Read and write policies
- List and manage auth methods
- Mount and configure secrets engines
- Create tokens for users
Installation
Option 1: Docker (Recommended)
# Production single-container deployment
docker run -d \
-p 3000:3000 \
-e DATABASE_URL="postgresql://..." \
-e VAULT_ADDR="http://vault:8200" \
-e VAULT_TOKEN="..." \
-e JWT_SECRET="..." \
-e NODE_ENV=production \
-e SERVE_FRONTEND=true \
ghcr.io/your-org/kluis:latest Option 2: From Source
# Clone repository
git clone https://github.com/your-org/kluis.git
cd kluis
# Install dependencies
cd backend && npm install
cd ../frontend && npm install
# Run database migrations
cd ../backend
npx prisma migrate deploy
# Build frontend
cd ../frontend && npm run build
# Start production server
cd ../backend
NODE_ENV=production SERVE_FRONTEND=true npm start Option 3: Development Mode
# Terminal 1: Backend
cd backend
npm install
npm run dev
# Terminal 2: Frontend
cd frontend
npm install
npm run dev Frontend runs on :5173, backend on :3000.
Configuration
Environment Variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string |
VAULT_ADDR | Yes | Vault server address |
VAULT_TOKEN | Yes | Vault authentication token |
JWT_SECRET | Yes | Secret for signing JWT tokens |
SESSION_SECRET | Yes | Secret for session cookies |
NODE_ENV | No | Environment (development/production) |
SERVE_FRONTEND | No | Serve frontend from backend (production) |
GOOGLE_CLIENT_ID | No | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | No | Google OAuth client secret |
GITHUB_CLIENT_ID | No | GitHub OAuth client ID |
GITHUB_CLIENT_SECRET | No | GitHub OAuth client secret |
Architecture
Kluis is a management layer that sits between your team and Vault:
Key Principles
- Secrets stay in Vault - Kluis never stores secrets
- Enhanced RBAC - Permissions Vault doesn't offer natively
- Automatic policies - Generated from role assignments
- Single source of truth - PostgreSQL for app state, Vault for secrets
Access Control
Permission System
Kluis provides 44 granular permissions across 11 resource types:
| Resource | Actions |
|---|---|
| KV Secrets | read, write, delete, admin |
| Database | read, create, revoke, admin |
| PKI | read, issue, revoke, admin |
| Policies | read, write, delete, admin |
| Engines | read, write, delete, admin |
| Users | read, write, delete, admin |
| Roles | read, write, delete, admin |
| Audit | read |
Built-in Roles
- SUPERADMIN - Full access to everything
- MAINTAINER - Administrative operations
- USER - Read-only access
- KV_ADMIN - KV engine administration
- DATABASE_OPERATOR - Database credential management
- PKI_OPERATOR - Certificate operations
- CUSTOM - User-defined permissions
Engine-Level Access
Beyond permissions, Kluis controls which specific engine instances each role can access:
// Grant "dev-team" role access to specific engines
POST /api/management/engines/:engineId/roles
{
"roleId": "dev-team",
"permissions": ["kv:read", "kv:write"]
} Policy Synchronization
Kluis automatically generates and syncs Vault policies based on role assignments.
How It Works
- Admin assigns role access to engine via UI
- Kluis queries accessible engines for that role
- HCL policy generated with correct paths
- Policy pushed to Vault
- User tokens reflect new permissions
Manual Resync
Force a complete policy resync:
POST /api/management/roles/resync-all Fault Tolerance
Sync is non-blocking. If Vault is temporarily unavailable, Kluis queues changes and syncs when connectivity returns.
API Reference
Kluis exposes a REST API. Full documentation available at /api-docs when running.
Authentication
POST /api/auth/login
{
"email": "[email protected]",
"password": "..."
}
// Response
{
"token": "eyJhbG...",
"refreshToken": "...",
"user": { ... }
} Secret Engines
// Read KV secret
GET /api/engines/kv-v2/secrets/path/to/secret
// Write KV secret
POST /api/engines/kv-v2/secrets/path/to/secret
{
"data": { "key": "value" }
}
// Generate database credentials
GET /api/engines/database/creds/:role
// Issue PKI certificate
POST /api/engines/pki/issue/:role
{
"common_name": "service.example.com"
} Management
// List roles
GET /api/management/roles
// Create custom role
POST /api/management/roles
{
"name": "custom-role",
"permissions": ["kv:read", "database:create"]
}
// Grant engine access
POST /api/management/engines/:id/roles
{
"roleId": "..."
}
// Query audit logs
GET /api/audit?user=alice&action=secrets:read&from=2024-01-01 Environment Variables
Core Settings
# Required
DATABASE_URL=postgresql://user:pass@host:5432/kluis
VAULT_ADDR=http://vault:8200
VAULT_TOKEN=your-token
JWT_SECRET=long-random-string
SESSION_SECRET=another-random-string
# Production
NODE_ENV=production
SERVE_FRONTEND=true
PORT=3000 OAuth Providers
# Google
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback
# GitHub
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback
# Azure AD
AZURE_CLIENT_ID=...
AZURE_CLIENT_SECRET=...
AZURE_TENANT_ID=...
AZURE_CALLBACK_URL=http://localhost:3000/api/auth/azure/callback Deployment
Production Checklist
- Set
NODE_ENV=production - Use strong, unique secrets for JWT and sessions
- Configure TLS termination (nginx, load balancer)
- Set up database backups
- Create dedicated Vault policy (not root token)
- Configure OAuth callback URLs for your domain
Docker Production
version: '3.8'
services:
kluis:
image: ghcr.io/your-org/kluis:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- SERVE_FRONTEND=true
- DATABASE_URL=postgresql://...
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=...
- JWT_SECRET=...
- SESSION_SECRET=...
depends_on:
- postgres
postgres:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_DB=kluis
- POSTGRES_USER=kluis
- POSTGRES_PASSWORD=...
volumes:
pgdata: Horizontal Scaling
Run multiple Kluis instances behind a load balancer. Ensure:
- All instances share the same database
- JWT_SECRET is identical across instances
- Sticky sessions if using in-memory session store
Security
Best Practices
- Use TLS - Always terminate TLS in production
- Rotate secrets - Periodically rotate JWT and session secrets
- Dedicated Vault token - Create a scoped policy for Kluis
- Network isolation - Keep Vault and PostgreSQL on private networks
- Regular updates - Keep dependencies updated
Kluis Vault Policy
Example minimal policy for the Kluis service token:
# Policy management
path "sys/policies/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Secrets engines
path "sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Auth methods
path "sys/auth/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Token creation
path "auth/token/create" {
capabilities = ["create", "update"]
}
# All secrets (adjust paths as needed)
path "secret/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}