pub struct RestrictiveHandoff { /* private fields */ }Expand description
A short-lived file holding a secret, readable only by this account, deleted on every path out.
05-infrastructure.md puts the encoded JIT configuration in a “restrictive
temporary file or process-safe handoff” and requires “Delete immediately
after runner start or failed start”. Both halves of that sentence are
implemented here: RestrictiveHandoff::create makes the file
unreadable by other local users at the moment of creation, and Drop
deletes it whether the launch succeeded, failed, or panicked.
§Why creation, not creation-then-chmod
On Windows the file is created through CreateFileW with an explicit
SECURITY_ATTRIBUTES, and on Unix through open(2) with mode 0600. In
both cases the restriction is applied by the call that creates the file.
Creating a file and then tightening it leaves a window — however short — in
which the JIT configuration exists on disk under whatever the parent
directory happened to grant, and a window is all a local attacker needs.
§What is not claimed
The file is deleted, not securely erased. No modern filesystem lets a userspace program guarantee that the bytes are unrecoverable — a journal, a copy-on-write snapshot, or an SSD’s wear levelling can each keep a copy that an overwrite never reaches. Claiming otherwise would be worse than saying so plainly, so the control this design relies on is the file’s short life and its access control, not erasure.
Implementations§
Source§impl RestrictiveHandoff
impl RestrictiveHandoff
Sourcepub const NAME_PREFIX: &'static str = "jit-"
pub const NAME_PREFIX: &'static str = "jit-"
What every handoff file’s name begins with.
Published because the name is otherwise a UUID nobody can predict, and
c3’s persistent cleanup has to answer “did an encoded configuration
survive into this slot?” after the process that owned it is gone
(04-security-recovery.md: JIT values are “never retained in slot”).
A second "jit-" spelled out over there would be a second source of
truth for the one fact that decides whether a secret is still on disk.
Sourcepub fn create(
directory: &Path,
payload: SecretString,
) -> Result<Self, HandoffError>
pub fn create( directory: &Path, payload: SecretString, ) -> Result<Self, HandoffError>
Writes payload to a new uniquely named file in directory.
The name is a UUID rather than a predictable one, so that another local account cannot pre-create the path and win the race for it; creation is exclusive, so if it did, this fails rather than writing into the squatter’s file.
§Errors
Sourcepub fn path(&self) -> &Path
pub fn path(&self) -> &Path
The path to hand to the child process.
This is the only thing that may reach a command line. The payload is not
exposed at all: it is held as a SecretString, which has no Display
and a redacting Debug, so it cannot be formatted into an argument by
accident.