1# spindle secrets with openbao
2
3This document covers setting up Spindle to use OpenBao for secrets
4management via OpenBao Proxy instead of the default SQLite backend.
5
6## overview
7
8Spindle now uses OpenBao Proxy for secrets management. The proxy handles
9authentication automatically using AppRole credentials, while Spindle
10connects to the local proxy instead of directly to the OpenBao server.
11
12This approach provides better security, automatic token renewal, and
13simplified application code.
14
15## installation
16
17Install OpenBao from nixpkgs:
18
19```bash
20nix shell nixpkgs#openbao # for a local server
21```
22
23## setup
24
25The setup process can is documented for both local development and production.
26
27### local development
28
29Start OpenBao in dev mode:
30
31```bash
32bao server -dev -dev-root-token-id="root" -dev-listen-address=127.0.0.1:8201
33```
34
35This starts OpenBao on `http://localhost:8201` with a root token.
36
37Set up environment for bao CLI:
38
39```bash
40export BAO_ADDR=http://localhost:8200
41export BAO_TOKEN=root
42```
43
44### production
45
46You would typically use a systemd service with a configuration file. Refer to
47[@tangled.sh/infra](https://tangled.sh/@tangled.sh/infra) for how this can be
48achieved using Nix.
49
50Then, initialize the bao server:
51```bash
52bao operator init -key-shares=1 -key-threshold=1
53```
54
55This will print out an unseal key and a root key. Save them somewhere (like a password manager). Then unseal the vault to begin setting it up:
56```bash
57bao operator unseal <unseal_key>
58```
59
60All steps below remain the same across both dev and production setups.
61
62### configure openbao server
63
64Create the spindle KV mount:
65
66```bash
67bao secrets enable -path=spindle -version=2 kv
68```
69
70Set up AppRole authentication and policy:
71
72Create a policy file `spindle-policy.hcl`:
73
74```hcl
75# Full access to spindle KV v2 data
76path "spindle/data/*" {
77 capabilities = ["create", "read", "update", "delete"]
78}
79
80# Access to metadata for listing and management
81path "spindle/metadata/*" {
82 capabilities = ["list", "read", "delete", "update"]
83}
84
85# Allow listing at root level
86path "spindle/" {
87 capabilities = ["list"]
88}
89
90# Required for connection testing and health checks
91path "auth/token/lookup-self" {
92 capabilities = ["read"]
93}
94```
95
96Apply the policy and create an AppRole:
97
98```bash
99bao policy write spindle-policy spindle-policy.hcl
100bao auth enable approle
101bao write auth/approle/role/spindle \
102 token_policies="spindle-policy" \
103 token_ttl=1h \
104 token_max_ttl=4h \
105 bind_secret_id=true \
106 secret_id_ttl=0 \
107 secret_id_num_uses=0
108```
109
110Get the credentials:
111
112```bash
113# Get role ID (static)
114ROLE_ID=$(bao read -field=role_id auth/approle/role/spindle/role-id)
115
116# Generate secret ID
117SECRET_ID=$(bao write -f -field=secret_id auth/approle/role/spindle/secret-id)
118
119echo "Role ID: $ROLE_ID"
120echo "Secret ID: $SECRET_ID"
121```
122
123### create proxy configuration
124
125Create the credential files:
126
127```bash
128# Create directory for OpenBao files
129mkdir -p /tmp/openbao
130
131# Save credentials
132echo "$ROLE_ID" > /tmp/openbao/role-id
133echo "$SECRET_ID" > /tmp/openbao/secret-id
134chmod 600 /tmp/openbao/role-id /tmp/openbao/secret-id
135```
136
137Create a proxy configuration file `/tmp/openbao/proxy.hcl`:
138
139```hcl
140# OpenBao server connection
141vault {
142 address = "http://localhost:8200"
143}
144
145# Auto-Auth using AppRole
146auto_auth {
147 method "approle" {
148 mount_path = "auth/approle"
149 config = {
150 role_id_file_path = "/tmp/openbao/role-id"
151 secret_id_file_path = "/tmp/openbao/secret-id"
152 }
153 }
154
155 # Optional: write token to file for debugging
156 sink "file" {
157 config = {
158 path = "/tmp/openbao/token"
159 mode = 0640
160 }
161 }
162}
163
164# Proxy listener for Spindle
165listener "tcp" {
166 address = "127.0.0.1:8201"
167 tls_disable = true
168}
169
170# Enable API proxy with auto-auth token
171api_proxy {
172 use_auto_auth_token = true
173}
174
175# Enable response caching
176cache {
177 use_auto_auth_token = true
178}
179
180# Logging
181log_level = "info"
182```
183
184### start the proxy
185
186Start OpenBao Proxy:
187
188```bash
189bao proxy -config=/tmp/openbao/proxy.hcl
190```
191
192The proxy will authenticate with OpenBao and start listening on
193`127.0.0.1:8201`.
194
195### configure spindle
196
197Set these environment variables for Spindle:
198
199```bash
200export SPINDLE_SERVER_SECRETS_PROVIDER=openbao
201export SPINDLE_SERVER_SECRETS_OPENBAO_PROXY_ADDR=http://127.0.0.1:8201
202export SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT=spindle
203```
204
205Start Spindle:
206
207Spindle will now connect to the local proxy, which handles all
208authentication automatically.
209
210## production setup for proxy
211
212For production, you'll want to run the proxy as a service:
213
214Place your production configuration in `/etc/openbao/proxy.hcl` with
215proper TLS settings for the vault connection.
216
217## verifying setup
218
219Test the proxy directly:
220
221```bash
222# Check proxy health
223curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/sys/health
224
225# Test token lookup through proxy
226curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/auth/token/lookup-self
227```
228
229Test OpenBao operations through the server:
230
231```bash
232# List all secrets
233bao kv list spindle/
234
235# Add a test secret via Spindle API, then check it exists
236bao kv list spindle/repos/
237
238# Get a specific secret
239bao kv get spindle/repos/your_repo_path/SECRET_NAME
240```
241
242## how it works
243
244- Spindle connects to OpenBao Proxy on localhost (typically port 8200 or 8201)
245- The proxy authenticates with OpenBao using AppRole credentials
246- All Spindle requests go through the proxy, which injects authentication tokens
247- Secrets are stored at `spindle/repos/{sanitized_repo_path}/{secret_key}`
248- Repository paths like `did:plc:alice/myrepo` become `did_plc_alice_myrepo`
249- The proxy handles all token renewal automatically
250- Spindle no longer manages tokens or authentication directly
251
252## troubleshooting
253
254**Connection refused**: Check that the OpenBao Proxy is running and
255listening on the configured address.
256
257**403 errors**: Verify the AppRole credentials are correct and the policy
258has the necessary permissions.
259
260**404 route errors**: The spindle KV mount probably doesn't exist - run
261the mount creation step again.
262
263**Proxy authentication failures**: Check the proxy logs and verify the
264role-id and secret-id files are readable and contain valid credentials.
265
266**Secret not found after writing**: This can indicate policy permission
267issues. Verify the policy includes both `spindle/data/*` and
268`spindle/metadata/*` paths with appropriate capabilities.
269
270Check proxy logs:
271
272```bash
273# If running as systemd service
274journalctl -u openbao-proxy -f
275
276# If running directly, check the console output
277```
278
279Test AppRole authentication manually:
280
281```bash
282bao write auth/approle/login \
283 role_id="$(cat /tmp/openbao/role-id)" \
284 secret_id="$(cat /tmp/openbao/secret-id)"
285```