Add keyring package for kernel-keyring-backed secret sealing #3
Reference in New Issue
Block a user
Delete Branch "benvin/keyring"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cached Vault tokens in vault-tools sit in plaintext under ~/.cache/vault. Sealing them with a key that lives only in the kernel keyring for the current boot and uid keeps them useless on disk; a lost key just means logging in again.
keyringpackage:Open,Seal,Unseal,Forgetuserkey in the user keyring, readable by the uid without possessionKEYCTL_MOVE_EXCLso concurrent first users converge on one keyErrKeyUnavailable/ErrCorrupterrors.ErrUnsupportedKEYCTL_LINKtheir own; LINK displaces, so the one that re-read (L97) before the other linked has already sealed with a dead key (Seal succeeds, blob is permanentlyErrKeyUnavailable; L97 comment is wrong). Reproduced: 2/160 blobs unreadable with 8 concurrent first Seals → useKEYCTL_MOVEwithKEYCTL_MOVE_EXCLfrom the thread keyring; onEEXISTre-get()the winner, and if that is stale (revoked/wrong size — still counts as existing) unlink it by id and retry.nilAAD inseal/unsealkeeps every test green → add a case that sets an unknown version/fingerprint with AAD on and asserts GCM rejects it (or drop the AAD claim from L43).make coverto 77.6% (below COVER_MIN 90), so a seccomp-restricted runner fails as "coverage below minimum" instead of skipping → fail instead of skip whenCIis set, or exclude keyring_linux.go from the gate.descis unused underGOOS!=linux(GOOS=darwin golangci-lint run ./keyring/→unused) → move the field/struct into keyring_linux.go and declare an emptyKeyringin keyring_other.go.|/keyring/keyring_linux.goexclusion rests on a false premise: CI test step (pipeline 7) reportskeyring coverage: 84.3%, identical to a keyctl-enabled host, so the tests do not skip there; the gate passes at 91.5% with the file included → drop the exclusion and the comment at Makefile:11-12.purgeDead()runs on plain ENOKEY (every first use per boot) and unlinks every revoked/expired key in@u, including other apps' keys the caller cannot even view (verified: 3/3 foreign dead keys removed, their search flips EKEYREVOKED→ENOKEY) → call it only when the search error is EKEYREVOKED/EKEYEXPIRED, the only case where a dead key of our description blocks MOVE_EXCL.sealed, _ := k.Seal(...)drops the error → check it.ci/woodpecker/pr/testfails on319ae21(make coverexit 2); with keyctl blocked the Kernel tests skip and total coverage is 71.6% < 90 → let the test step run keyctl (unconfined seccomp) so the keyring tests execute in CI; do not restore the exclusionOpenhides permission bugs: dropping the possessor link bit fromkeyPermmakes all 8 Kernel tests SKIP and the package pass → decide the skip with a raw keyctl probe (e.g.KEYCTL_GET_KEYRING_IDon@u), thent.Fatalon anyOpenerrorpurgeDead(keyring_linux.go:113) also unlinks other apps' revoked/expired keys → reword the bulletNo findings.