feat: add cross-invocation passphrase caching via Linux keyring (v0.3.0)

Use keyctl (keyutils) to cache the master passphrase in the kernel keyring
with a configurable TTL (default 5 min). New unlock/lock subcommands for
manual cache control. keyctl is optional — silently skipped if not installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 05:04:07 +01:00
parent e70596cd59
commit 242eeca238
3 changed files with 221 additions and 6 deletions

View File

@@ -10,6 +10,45 @@ authentication detection (bearer token or HTTP basic auth).
---
## What Changed (Session 2026-03-01b)
### Passphrase caching via Linux keyring (v0.3.0)
Added cross-invocation master passphrase caching using the Linux kernel keyring
(`keyctl` from `keyutils`). `keyctl` is an **optional** dependency — if not
installed, behaviour is unchanged (prompt every time).
**New functions** (Encryption helpers section):
| Function | Purpose |
| --- | --- |
| `_keyring_available()` | Returns 0 if `keyctl` is installed |
| `_keyring_get()` | Read cached passphrase from user keyring; return 1 if missing/expired |
| `_keyring_set(pass, ttl)` | Store passphrase in keyring with TTL |
| `_keyring_clear()` | Revoke the cached key |
**New subcommands**:
| Command | Purpose |
| --- | --- |
| `unlock` | Prompt for passphrase and cache in keyring (useful before scripting) |
| `lock` | Clear cached passphrase from keyring immediately |
**New global option**: `--cache-timeout <seconds>` (default 300 / 5 min).
Also configurable via `CACHE_TIMEOUT=<seconds>` in `hubmanager.conf`.
**New constants**: `HM_KEYRING_KEY`, `HM_KEYRING_DEFAULT_TIMEOUT`.
**New global variable**: `HM_CACHE_TIMEOUT`.
**Modified functions**:
- `_prompt_master_pass()` — checks keyring before prompting; stores after prompt
- `_prompt_set_master_pass()` — stores in keyring after confirmation
**Version bump**: `0.2.1``0.3.0`
---
## What Changed (Session 2026-03-01)
### Bug fixes and improvements (v0.2.1)
@@ -105,6 +144,8 @@ Both `load_config()` and `resolve_registry_alias()` detect the prefix and call
| `delete` | Resolve tag → digest → `DELETE`; requires confirmation or `--yes` |
| `copy` | Copy/retag within or across registries; blob mount for same-registry retag |
| `prune` | Delete outdated tags sorted by image creation date |
| `unlock` | Cache master passphrase in kernel keyring (requires `keyctl`) |
| `lock` | Clear cached master passphrase from keyring |
### Supporting files
@@ -126,11 +167,12 @@ set -euo pipefail
# --- Output / Formatting helpers ---
# --- Dependency check ---
# --- Encryption helpers --- _encrypt_value(), _decrypt_value(), _prompt_master_pass()
# _keyring_get(), _keyring_set(), _keyring_clear()
# --- Config loading ---
# --- HTTP helpers --- raw_http(), get_response_header()
# --- Authentication --- probe_registry_auth(), get_bearer_token(), make_auth_header()
# --- Registry request --- registry_request() ← main HTTP wrapper
# --- Subcommands --- cmd_login/list/tags/inspect/delete/copy/prune
# --- Subcommands --- cmd_login/unlock/lock/list/tags/inspect/delete/copy/prune
# --- Global arg parsing --- parse_global_args()
# --- Main dispatcher --- main()
main "$@"
@@ -184,6 +226,14 @@ main "$@"
`HM_MASTER_PASS`).
- `openssl` is an optional dependency: not checked at startup, only on first `enc:` encounter.
10. **Passphrase caching** (v0.3.0):
- Uses Linux kernel keyring (`keyctl` from `keyutils`) to cache the master passphrase
across invocations with a configurable TTL (default 300s / 5 min).
- `_prompt_master_pass()` checks keyring before prompting; stores after prompt.
- `keyctl` is optional: if not installed, each invocation prompts as before (no errors).
- `unlock` pre-caches the passphrase; `lock` clears it immediately.
- TTL configurable via `--cache-timeout <seconds>` flag or `CACHE_TIMEOUT` config key.
---
## Global Variables (key ones)
@@ -199,6 +249,7 @@ main "$@"
| `HM_TOKEN_CACHE["registry\|scope"]` | Cached bearer token |
| `HM_TOKEN_EXPIRY["registry\|scope"]` | Token expiry (epoch seconds) |
| `HM_MASTER_PASS` | Master passphrase for `enc:` config values (session-cached) |
| `HM_CACHE_TIMEOUT` | Keyring cache TTL in seconds (default 300) |
| `HM_LAST_HTTP_CODE` | HTTP status of most recent request |
| `HM_LAST_HEADERS_FILE` | Temp file path with response headers |
| `HM_TMPFILES` | Array of temp files, cleaned up via `trap EXIT` |