Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

πŸ” Vault Clone - Redacted Secret Browser with Approval Workflow

Browse OpenBao/HashiCorp Vault secrets without exposing values. Request secret updates via Slack approval workflow.

License: MIT Python 3.11+ FastAPI Docker

Let your team see secret KEYS without exposing VALUES. Update secrets via approval workflow.


🎯 The Problem

OpenBao/Vault has a major UX issue: There's no way to show users which secrets exist (keys) without also showing the secret values.

This creates a dilemma:

  • ❌ Option 1: Give team access to Vault β†’ They see ALL secret values (security risk!)
  • ❌ Option 2: Lock down Vault β†’ Team has no visibility, constantly asks "what secrets exist?"
  • ❌ Option 3: Maintain a separate documentation β†’ Gets out of sync instantly

Real-world scenario:

  • Developer: "Does the prod/api/ path have a DATABASE_URL secret?"
  • DevOps: Has to manually check Vault and tell them
  • Developer: "Can you add a REDIS_URL secret?"
  • DevOps: Manually adds it

This doesn't scale.


✨ The Solution

Vault Clone solves this with:

  1. πŸ“– Read-Only Mirror: Auto-syncs secret paths and keys from OpenBao/Vault every 5 minutes
  2. πŸ”’ Redacted Values: Shows secret keys but redacts all values (shows only first/last char hints)
  3. πŸ” Searchable UI: Team can browse, search, filter all secrets without security risk
  4. βœ… Self-Serve Updates: Request new secrets via web form β†’ Triggers Slack approval β†’ Auto-executes
  5. πŸ” Google OAuth SSO: Only authorized domain can access
  6. 🎯 Zero Secrets Stored: Values are redacted at ingest, never stored in memory or disk

Security Guarantee

# Values are redacted BEFORE storage - original values NEVER touch this app
def redact_value(value: str) -> str:
    """Redact secret, showing only first/last character hints."""
    if len(value) <= 4:
        return "***"
    return f"{value[0]}***{value[-1]}"  # e.g., "p***d" for "password123"

Even if someone hacks this app's database, they get nothing.


πŸš€ How It Works

1. Automatic Sync (Every 5 Minutes)

OpenBao Vault (read-only token)
     ↓
Sync Service (list + read secrets)
     ↓
Redaction Engine (values β†’ "p***d")
     ↓
Local Database (keys + redacted values)

2. Team Browses Secrets

User β†’ Google SSO Login β†’ Browse UI
  ↓
Search: "DATABASE_URL"
  ↓
Results show:
  βœ… Path: prod/api/config
  βœ… Key: DATABASE_URL
  βœ… Value: p***d (redacted)
  βœ… Last Updated: 2026-06-20

Team can now:

  • See which secrets exist
  • Know the exact secret key names
  • Know where secrets are located
  • Search across all secrets
  • WITHOUT seeing actual values!

3. Request New Secret

User clicks "Add Secret" β†’ Fill form:
  - Path: prod/api/config
  - Key: REDIS_URL
  - Value: redis://...
  - Reason: "New caching layer for API"
     ↓
Form submits to Approval Handler (Slack integration)
     ↓
Approver gets Slack message with [Approve] [Reject]
     ↓
On approval β†’ Argo Workflow executes β†’ Adds secret to OpenBao
     ↓
Next sync (5 min) β†’ New secret appears in Vault Clone

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  OpenBao/Vault (Production)             β”‚
β”‚  Read-only token: Can list + read all secrets          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ (every 5 min sync)
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Vault Clone (This App)                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Sync Service β”‚β†’ β”‚   Redaction   β”‚β†’ β”‚   Database   β”‚ β”‚
β”‚  β”‚ (httpx async)β”‚  β”‚    Engine     β”‚  β”‚   (in-mem)   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Web UI (FastAPI + Jinja2 templates)             β”‚  β”‚
β”‚  β”‚  - Browse/search secrets                         β”‚  β”‚
β”‚  β”‚  - View redacted values                          β”‚  β”‚
β”‚  β”‚  - Add Secret form                               β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ (Add Secret request)
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Approval Handler (Slack Integration)          β”‚
β”‚  Sends approval request to Slack β†’ Approvers decide    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ (On approval)
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Argo Workflow (Auto-Execution)                β”‚
β”‚  Executes: vault kv put secret/path key=value          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”’ Security Features

1. Redaction at Ingest

# BEFORE storage - values never exist in raw form
value = vault.read("secret/prod/db")["password"]  # "SuperSecret123"
redacted = redact_value(value)                    # "S***3"
db.store(key="password", value=redacted)          # Only "S***3" stored

Original value never touches:

  • Application memory (beyond read β†’ redact)
  • Database
  • Logs
  • Response payloads

2. Google OAuth SSO

  • Only users from ALLOWED_EMAIL_DOMAIN can log in
  • Session-based authentication (7-day expiry)
  • Signed cookies (no database needed)

3. Read-Only Vault Token

The app uses a read-only Vault token:

  • βœ… Can list secret paths
  • βœ… Can read secret values (for sync only)
  • ❌ Cannot write/update/delete secrets
  • ❌ Cannot create policies
  • ❌ Cannot modify ACLs

All writes go through Approval Handler β†’ Argo Workflow.

4. No Secrets in Logs

logger.info(f"Synced secret: {path}/{key}")  # βœ… OK - no value
logger.info(f"Value: {value}")                # ❌ NEVER - would leak

5. Approval Workflow Integration

Updates require:

  1. Slack approval from authorized approvers
  2. Optional ticket requirement (Jira/Linear)
  3. Multi-level approvals for production paths
  4. Full audit trail (who requested, who approved, when executed)

πŸ“¦ Features

Core Capabilities

  • βœ… Auto-Sync: Mirrors all secret paths/keys every 5 minutes
  • βœ… Redacted Values: Shows "p***d" instead of "password123"
  • βœ… Search & Filter: Find secrets across all paths
  • βœ… Path Hierarchy: Browse secrets by folder structure
  • βœ… Self-Serve Add Secret: Request form β†’ Slack approval β†’ Auto-execution
  • βœ… Google OAuth SSO: Domain-restricted access
  • βœ… Responsive UI: Modern web interface (Jinja2 templates)
  • βœ… Health Checks: /health endpoint for monitoring

Secret Display

For each secret, shows:

  • πŸ”‘ Key Name: Exact secret key
  • πŸ“ Path: Where secret is located
  • πŸ‘οΈ Redacted Value: First/last char hints (e.g., "A***k" for base64)
  • πŸ“… Last Sync: When this secret was last updated from Vault
  • 🏷️ Version: Secret version number (if supported)

Add Secret Form

Users can request:

  • Path: prod/api/config
  • Key: NEW_API_KEY
  • Value: (actual secret - sent only to approver via Slack DM)
  • Description: Why this secret is needed
  • Ticket ID: Optional Jira/Linear ticket

πŸš€ Quick Start

Prerequisites

  • Python 3.11+
  • OpenBao/HashiCorp Vault instance
  • Read-only Vault token
  • Google OAuth credentials (for SSO)
  • Approval Handler deployed (optional, for self-serve updates)

1. Clone Repository

git clone https://github.com/code-rajeshdeb/vault-clone.git
cd vault-clone

2. Install Dependencies

pip install -r requirements.txt

3. Configure Environment

# Vault Configuration
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN_FILE=/path/to/token  # OR set VAULT_TOKEN env var
export VAULT_MOUNT=secret               # Vault KV mount point
export SYNC_INTERVAL=300                # Sync every 5 minutes

# Google OAuth (for SSO)
export GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=your-secret
export GOOGLE_REDIRECT_URI=https://vault-clone.example.com/auth/google/callback
export ALLOWED_EMAIL_DOMAIN=example.com  # Only @example.com emails allowed

# Approval Handler (for Add Secret feature)
export APPROVAL_HANDLER_URL=http://approval-handler:8080

# Session Secret (generate with: openssl rand -hex 32)
export SESSION_SECRET_KEY=your-session-secret-key

4. Run Application

# Development
uvicorn main:app --reload --host 0.0.0.0 --port 8000

# Production
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

5. Access Web UI

  1. Open http://localhost:8000
  2. Click "Login with Google"
  3. Authenticate with your @example.com email
  4. Browse secrets!

🐳 Docker Deployment

Build Image

docker build -t vault-clone:latest .

Run Container

docker run -d \
  -p 8000:8000 \
  -e VAULT_ADDR=https://vault.example.com \
  -e VAULT_TOKEN=your-readonly-token \
  -e GOOGLE_CLIENT_ID=your-client-id \
  -e GOOGLE_CLIENT_SECRET=your-secret \
  -e ALLOWED_EMAIL_DOMAIN=example.com \
  vault-clone:latest

☸️ Kubernetes Deployment

Manifests are in k8s/:

# Update k8s/external-secret-*.yaml with your secret manager paths
# Update k8s/deployment.yaml with your config

kubectl apply -f k8s/

Included:

  • deployment.yaml - Main app deployment
  • service.yaml - ClusterIP service
  • external-secret-openbao-readonly-token.yaml - Read-only Vault token
  • external-secret-google-oauth.yaml - Google OAuth credentials

🎨 How to Use

As a Team Member

Browse Secrets:

  1. Login with Google SSO
  2. Browse secret paths in sidebar
  3. Search for specific keys
  4. View redacted values (e.g., "A***k")
  5. Know which secrets exist without seeing values!

Request New Secret:

  1. Click "Add Secret" button
  2. Fill form:
    • Path: prod/api/config
    • Key: NEW_SECRET_KEY
    • Value: (your secret value)
    • Reason: Why you need this
  3. Submit β†’ Goes to Slack for approval
  4. Wait for approval β†’ Secret auto-added to Vault
  5. Next sync (5 min) β†’ Appears in Vault Clone

As an Approver

  1. Receive Slack message: "New secret request from @john.doe"
  2. See details:
    • Path, key, actual value (in Slack DM only!)
    • Requester, reason, ticket
  3. Click [Approve] or [Reject]
  4. On approve β†’ Argo Workflow executes β†’ Secret added

πŸ”§ Configuration

Vault Token Permissions

Create a read-only policy:

# vault-clone-readonly.hcl
path "secret/*" {
  capabilities = ["read", "list"]
}

path "secret/metadata/*" {
  capabilities = ["read", "list"]
}

Apply:

vault policy write vault-clone-readonly vault-clone-readonly.hcl
vault token create -policy=vault-clone-readonly -period=720h

Google OAuth Setup

  1. Go to Google Cloud Console
  2. Create OAuth 2.0 credentials
  3. Add authorized redirect URI:
    https://your-domain.com/auth/google/callback
    
  4. Copy Client ID and Secret
  5. Set as environment variables

Sync Interval

Adjust via SYNC_INTERVAL (seconds):

  • 300 (5 min) - Default, good balance
  • 60 (1 min) - More frequent updates (higher Vault load)
  • 600 (10 min) - Less frequent (lower load)

πŸ§ͺ Testing

Unit Tests

pytest tests/ -v

Health Check

curl http://localhost:8000/health
# {"status":"healthy","vault_connected":true,"last_sync":"2026-06-28T10:30:00Z"}

πŸ”’ Security Best Practices

  1. βœ… Use read-only Vault token - Never give write access
  2. βœ… Rotate token regularly - Set token TTL/period
  3. βœ… Domain-restrict SSO - Only allow company email domain
  4. βœ… HTTPS only - Always use TLS for production
  5. βœ… Network policies - Restrict pod egress to Vault + Google only
  6. βœ… Audit logs - Monitor who accesses which secrets
  7. βœ… Secret rotation - Use Vault's TTL for automatic expiry

🀝 Contributing

Contributions welcome!

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/amazing-feature)
  3. Commit changes (git commit -m 'Add amazing feature')
  4. Push to branch (git push origin feature/amazing-feature)
  5. Open Pull Request

πŸ“„ License

MIT License - see LICENSE file.


πŸ’– Support This Project

If you find this useful:

  • ⭐ Star this repo
  • πŸ› Report bugs via Issues
  • πŸ’‘ Suggest features
  • πŸ’° Sponsor via GitHub Sponsors

πŸ™ Related Projects


πŸ—ΊοΈ Roadmap

  • v2.0: Support for multiple Vault backends
  • v2.1: AWS Secrets Manager integration
  • v2.2: Secret diff view (track changes over time)
  • v2.3: Role-based access control (RBAC)
  • v2.4: Bulk secret operations
  • v2.5: Secret usage analytics

πŸ“ž Contact


Made with ❀️ for DevOps/SRE teams who need secret visibility without security risks.

⭐ Star this repo if it solves your Vault UX problem!

About

Browse OpenBao/Vault secrets with redacted values. Self-serve secret updates via Slack approval workflow. Solves the Vault UX problem: see keys without exposing values.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages