Skip to content

Create a New Project Secrets Folder

This guide explains how to create a new secrets folder for your project and scope access to a specific Keycloak group.

Prerequisites

  • OpenBao CLI installed and configured (Setup Guide)
  • Authenticated with admin role or root token
  • A Keycloak group for your project (or permission to create one)

Overview

Setting up a new project involves:

  1. Creating the secrets path/folder
  2. Creating a policy to control access
  3. Creating an external group mapping to Keycloak
  4. Testing access

Step 1: Plan Your Structure

Decide on your secrets path structure:

clusters/
├── hetzner-mgmt/           # Management cluster
│   ├── apps/
│   │   ├── frontend/
│   │   └── backend/
│   ├── databases/
│   └── external-services/
├── production/             # Production cluster
└── staging/                # Staging cluster

Step 2: Create the Secrets Path

Create an initial secret to establish the path:

# Login as admin
bao login -method=oidc role=admin

# Create the project folder with metadata
bao kv put clusters/hetzner-mgmt/apps/myproject/info \
  description="My Project secrets" \
  owner="myteam" \
  created="$(date -Iseconds)"

Step 3: Create Access Policy

Create a policy file that defines what access the group will have:

bao policy write myproject-admin - <<EOF
# Policy for myproject administrators
# Full access to myproject secrets

# Read and write secrets
path "clusters/data/hetzner-mgmt/apps/myproject/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Manage metadata (versions, etc.)
path "clusters/metadata/hetzner-mgmt/apps/myproject/*" {
  capabilities = ["list", "read", "delete"]
}

# List the project folder
path "clusters/data/hetzner-mgmt/apps/myproject" {
  capabilities = ["list"]
}

path "clusters/metadata/hetzner-mgmt/apps/myproject" {
  capabilities = ["list"]
}
EOF

Policy Capabilities Reference

Capability Description
create Create new secrets
read Read secret values
update Update existing secrets
delete Soft delete secrets
list List secrets in a path
sudo Access protected paths

Read-Only Policy Example

For users who only need to read secrets:

bao policy write myproject-readonly - <<EOF
# Read-only policy for myproject
path "clusters/data/hetzner-mgmt/apps/myproject/*" {
  capabilities = ["read", "list"]
}

path "clusters/metadata/hetzner-mgmt/apps/myproject/*" {
  capabilities = ["list", "read"]
}

path "clusters/data/hetzner-mgmt/apps/myproject" {
  capabilities = ["list"]
}
EOF

Step 4: Create External Group Mapping

Map a Keycloak group to the OpenBao policy:

4.1 Get the OIDC Accessor

OIDC_ACCESSOR=$(bao auth list -format=json | jq -r '.["oidc/"].accessor')
echo "OIDC Accessor: $OIDC_ACCESSOR"

4.2 Create External Group

bao write identity/group \
  name="myproject-admins" \
  type="external" \
  policies="myproject-admin"

4.3 Get the Group ID

GROUP_ID=$(bao read -format=json identity/group/name/myproject-admins | jq -r '.data.id')
echo "Group ID: $GROUP_ID"

4.4 Create Group Alias

Map the OpenBao group to the Keycloak group name:

bao write identity/group-alias \
  name="myproject-admins" \
  mount_accessor="$OIDC_ACCESSOR" \
  canonical_id="$GROUP_ID"

Keycloak Group Name

The name in the group-alias must match exactly the Keycloak group name (case-sensitive).

Step 5: Create Keycloak Group (if needed)

If the Keycloak group doesn't exist, create it:

Via Keycloak API

# Get admin token
export ADMIN_PASS='your-password'
ADMIN_TOKEN=$(curl -s -X POST "http://localhost:8080/realms/skatzi/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=dunk" \
  -d "password=$ADMIN_PASS" \
  -d "grant_type=password" \
  -d "client_id=admin-cli" | jq -r '.access_token')

# Create group
curl -X POST "http://localhost:8080/admin/realms/skatzi/groups" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "myproject-admins",
    "path": "/myproject-admins"
  }'

Add Users to Group

# Get user ID
USER_ID=$(curl -s -X GET "http://localhost:8080/admin/realms/skatzi/users?username=someuser" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.[0].id')

# Get group ID
KC_GROUP_ID=$(curl -s -X GET "http://localhost:8080/admin/realms/skatzi/groups?search=myproject-admins" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.[0].id')

# Add user to group
curl -X PUT "http://localhost:8080/admin/realms/skatzi/users/$USER_ID/groups/$KC_GROUP_ID" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Step 6: Test Access

Test as Group Member

# Login as a user in the myproject-admins group
bao login -method=oidc role=default

# Try to create a secret
bao kv put clusters/hetzner-mgmt/apps/myproject/test message="hello"

# Try to read it
bao kv get clusters/hetzner-mgmt/apps/myproject/test

# Try to list secrets
bao kv list clusters/hetzner-mgmt/apps/myproject/

Verify Access Denied for Others

Login as a user NOT in the group:

bao login -method=oidc role=default

# This should fail with "permission denied"
bao kv get clusters/hetzner-mgmt/apps/myproject/test

Complete Example Script

Here's a complete script to set up a new project:

#!/bin/bash
set -e

PROJECT_NAME="myproject"
KEYCLOAK_GROUP="myproject-admins"
SECRETS_PATH="clusters/hetzner-mgmt/apps/${PROJECT_NAME}"

echo "Setting up secrets for: $PROJECT_NAME"

# 1. Create initial secret/folder
echo "Creating secrets path..."
bao kv put ${SECRETS_PATH}/info \
  description="${PROJECT_NAME} secrets" \
  created="$(date -Iseconds)"

# 2. Create policy
echo "Creating policy..."
bao policy write ${PROJECT_NAME}-admin - <<EOF
path "clusters/data/hetzner-mgmt/apps/${PROJECT_NAME}/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
path "clusters/metadata/hetzner-mgmt/apps/${PROJECT_NAME}/*" {
  capabilities = ["list", "read", "delete"]
}
path "clusters/data/hetzner-mgmt/apps/${PROJECT_NAME}" {
  capabilities = ["list"]
}
path "clusters/metadata/hetzner-mgmt/apps/${PROJECT_NAME}" {
  capabilities = ["list"]
}
EOF

# 3. Get OIDC accessor
OIDC_ACCESSOR=$(bao auth list -format=json | jq -r '.["oidc/"].accessor')

# 4. Create external group
echo "Creating external group..."
bao write identity/group \
  name="${KEYCLOAK_GROUP}" \
  type="external" \
  policies="${PROJECT_NAME}-admin"

# 5. Get group ID and create alias
GROUP_ID=$(bao read -format=json identity/group/name/${KEYCLOAK_GROUP} | jq -r '.data.id')
bao write identity/group-alias \
  name="${KEYCLOAK_GROUP}" \
  mount_accessor="$OIDC_ACCESSOR" \
  canonical_id="$GROUP_ID"

echo "✅ Setup complete!"
echo ""
echo "Secrets path: ${SECRETS_PATH}/"
echo "Policy: ${PROJECT_NAME}-admin"
echo "Keycloak group: ${KEYCLOAK_GROUP}"
echo ""
echo "Next steps:"
echo "1. Ensure the Keycloak group '${KEYCLOAK_GROUP}' exists"
echo "2. Add users to the Keycloak group"
echo "3. Users can login with: bao login -method=oidc role=default"

Cleanup

To remove a project's access:

# Delete the group alias
bao delete identity/group-alias/id/<alias-id>

# Delete the external group
bao delete identity/group/name/myproject-admins

# Delete the policy
bao policy delete myproject-admin

# Optionally delete the secrets (permanent!)
bao kv metadata delete clusters/hetzner-mgmt/apps/myproject/info

Next Steps