workshopctl: add systemd secret retrieval - #996
Open
tlm wants to merge 5 commits into
Open
Conversation
stdinReadLimit is a fixed protection against buffering too much stdin data into the request body, so express it as a const with an underscore-separated literal rather than a var. Document why the limit exists: stdin is currently buffered in full rather than streamed, so the request body size must be bounded. The var only existed so tests could mock the limit down to 10 bytes. Drop MockStdinReadLimit and have the read-limit tests exercise the real 4MB boundary instead; the fixtures allocate a single slice and run in milliseconds, so the mock added indirection without benefit.
ConstError is a string-based error type for declaring sentinel errors as constants. Unlike sentinels created with errors.New, a ConstError cannot be reassigned and is matched by errors.Is through string equality, so each message must be unique. Convert ErrorNoWaitingChange to the new type as its first user.
Introduce local interception in workshopctl: invocations are inspected before being forwarded to the daemon, allowing subcommands to alter the request and install a response handler that produces the process exit code. The default handler preserves the existing pass-through behaviour. Use this for get-secret --systemd, invoked by the workshop-secret socket unit with the accepted connection on stdin. The LoadCredential peer address is decoded locally to identify the requesting unit, sdk and secret, then forwarded to the daemon as a regular get-secret invocation with stdin cleared, since the connection was consumed locally. The response handler maps daemon errors to the exit codes defined by the secrets spec: plug not connected yields a zero-byte credential (exit 0), secret not found exits 1, locked provider exits 2, and any other error exits 255. Support this in the client by splitting the workshopctl options from the wire type: WorkshopCtlPostData now defines the JSON transport with flat members, and WorkshopCtlOptions carries Stdin as an io.Reader so RunWorkshopctl takes a single options argument. Add ConstError sentinels for the plug-not-connected, secret-not-found and provider-locked outcomes so the handler can match them with errors.Is.
Add the daemon side of workshopctl get-secret: the subcommand resolves a secret identified as "<sdk>.<secret>" and writes the value to stdout. Resolution via workshopd is not implemented yet, so a hard-coded placeholder value is returned; the requested identifier is recorded with a debug log (never the value). Allow get-secret to run without root, as both the socket-activated --systemd service and SDK wrapper scripts invoke workshopctl as the workshop user.
These are convenience wrappers where callers only care about success or failure. Returning the byte count from fmt.Fprintf serves no purpose at this level and was causing the unparam linter to flag printf once it gained its first non-test caller.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add the
workshopctl get-secretplumbing used by systemdLoadCredential=requests.The implementation has two paths:
workshopctl get-secret <sdk>.<secret>forwards a secret request tothe daemon.
workshopctl get-secret --systemdhandles a request from the Workshopsecret socket. It reads the peer address of the accepted Unix connection to
identify the requesting systemd unit and credential, then forwards a normal
<sdk>.<secret>request to the daemon.The systemd path writes only credential bytes to stdout. Diagnostics go to
stderr, allowing the socket-activated unit to route them to the journal
without contaminating the credential value.
Systemd behaviour
A systemd peer address has the form:
For example:
workshopctldecodes this into the requesting unit, SDK, and secret plug.The systemd response handler implements the secret-spec exit codes:
0122550Current scope
The daemon command currently returns the placeholder value:
It logs the requested secret identifier but never its value. Host secret
provider lookup and secret-interface connection resolution are follow-up work.
This PR depends on the base branch's Workshop secret socket units.
Client refactor
WorkshopCtlOptionsnow owns its optional stdin reader. This lets localinterceptors consume or clear stdin before forwarding the request to the
daemon. The systemd interceptor clears it after reading the peer address, so
the accepted socket is not buffered into the API request body.
Testing
Added coverage for:
get-secretinvocation and non-root access;get-secret --systemd;Validated with:
Manual QA
Requires a Workshop build containing both this PR and the Workshop secret
socket units.
Launch a new workshop, then enter it as root.
Confirm the resolver socket is active:
Expected output:
Create a one-shot system service that requests a credential and writes it
to a temporary file:
Confirm systemd received the credential:
Expected output for the current placeholder implementation:
Confirm the socket-activated resolver processed the request:
systemctl list-units --all 'workshop-secret@*.service'Copy an instance name from the output, then inspect its log:
The log should identify the requesting unit, SDK, and secret name, but must
not include the credential value.
Confirm the plain command is authorised as the Workshop user:
Expected output:
This verifies the success path and systemd transport only. Host keyring lookup,
plug connection state, missing secrets, and locked providers require the future
daemon-side secret resolver.
Docs