Tutorial: temporary access¶
Goal¶
At the end you will see that a wrapped token works once, that a session ends when its token expires, that the server fails predictably without a token, and that when a token expires, OpenBao revokes everything it created.
Every command and output below comes from a real run, recorded in demo/tutorials/access/expected/ and checked by run.sh --check.
Run it¶
bash demo/tutorials/access/run.sh all
bash demo/tutorials/access/run.sh --check
Script: demo/tutorials/access/run.sh.
1. A wrapped token opens one session¶
The first server unwraps the token. The second start with the same value fails: the vault refuses to unwrap it again. A wrapped token that leaks after use is worthless, and a token that was intercepted before use shows up as this failure.
# A wrapped token opens one session; reusing it fails
$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_status
{
"addr": "http://127.0.0.1:18200",
"initialized": true,
"kv_mount": "secret",
"scope": "my-project",
"sealed": false,
"server_version": "2.7.1",
"token_expire_time": "<time>",
"token_policies": [
"default",
"my-project-read"
],
"token_renewable": true,
"token_ttl_seconds": <ttl>
}
exit_code=0
$ BAO_WRAPPING_TOKEN=<same wrapped token> khcall keyholder_status
{
"addr": "http://127.0.0.1:18200",
"initialized": true,
"kv_mount": "secret",
"scope": "my-project",
"sealed": false,
"server_version": "2.7.1",
"session_error": "unwrap token: vault returned HTTP 400 (already used or expired?)",
"token_policies": null,
"token_renewable": false,
"token_ttl_seconds": 0
}
--- stderr
<timestamp> session not opened: unwrap token: vault returned HTTP 400 (already used or expired?)
exit_code=0
2. A 5-second token expires mid-session¶
One session: the first call works, +wait=7s pauses, then keyholder_status reports the expiry and the next call is refused with a message that tells the agent what to do.
# A 5-second token: the call works, then the same session is refused
$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_run '{"command":"curl","args":["-sS","-H","Authorization: Bearer {{vault://my-project/dev/API_TOKEN}}","http://127.0.0.1:18080/"]}' +wait=7s keyholder_status keyholder_run '{"command":"curl","args":["-sS","-H","Authorization: Bearer {{vault://my-project/dev/API_TOKEN}}","http://127.0.0.1:18080/"]}'
### keyholder_run
{
"exit_code": 0,
"refs_used": [
"vault://my-project/dev/API_TOKEN"
],
"stderr": "",
"stdout": "api says: hello, caller with header Authorization: Bearer [hidden:vault://my-project/dev/API_TOKEN]\n"
}
### keyholder_status
{
"addr": "http://127.0.0.1:18200",
"initialized": true,
"kv_mount": "secret",
"scope": "my-project",
"sealed": false,
"server_version": "2.7.1",
"session_error": "token expired; renew access outside the chat",
"token_policies": null,
"token_renewable": false,
"token_ttl_seconds": 0
}
### keyholder_run
error: vault://my-project/dev/API_TOKEN: read my-project/dev: access denied by the vault: token expired or revoked, or the policy does not allow this path; renew access outside the chat
--- stderr
<timestamp> audit cmd="curl" refs=[vault://my-project/dev/API_TOKEN] exit=0 dur=<duration> status=ok
<timestamp> audit cmd="curl" refs=[] exit=0 dur=<duration> status=error: vault://my-project/dev/API_TOKEN: read my-project/dev: access denied by the vault: token expired or revoked, or the policy does not allow this path; renew access outside the chat
exit_code=1
3. No token¶
The server still starts, so the agent can call keyholder_status and learn why nothing works. The other tools refuse.
# Without a token the server starts, explains, and refuses
$ env -u BAO_TOKEN -u BAO_WRAPPING_TOKEN khcall keyholder_status keyholder_list
### keyholder_status
{
"addr": "http://127.0.0.1:18200",
"initialized": true,
"kv_mount": "secret",
"scope": "my-project",
"sealed": false,
"server_version": "2.7.1",
"session_error": "no token: set BAO_WRAPPING_TOKEN (preferred) or BAO_TOKEN",
"token_policies": null,
"token_renewable": false,
"token_ttl_seconds": 0
}
### keyholder_list
error: no session with the vault; a human must renew access outside the chat
--- stderr
<timestamp> session not opened: no token: set BAO_WRAPPING_TOKEN (preferred) or BAO_TOKEN
exit_code=1
4. Expiry revokes what the token created¶
Keyholder revokes the leases of a run when the command ends. If that cleanup never happens, because the server was killed or the machine went down, the token's own expiry still takes them away. Here a 5-second token issues a certificate whose lease would last 1 hour; when the token expires, the lease is gone with it. The certificate comes from OpenBao's PKI engine, so the case needs no external service.
# A 5-second token creates a 1-hour lease; when the token expires, the lease goes with it
$ BAO_TOKEN=<5-second token> bao write -field=lease_id pki/issue/agent common_name=agent.example.test ttl=1h
pki/issue/agent/<id>
exit_code=0
Leases under pki/issue/agent before the token expires: 1
$ BAO_TOKEN=<5-second token> bao token lookup
--- stderr
Error looking up token: Error making API request.
URL: GET http://127.0.0.1:18200/v1/auth/token/lookup-self
Code: 403. Errors:
* permission denied
exit_code=2
Leases under pki/issue/agent after the token expired: 0