Skip to content

tufclient: authenticate repo requests as this machine - #82

Open
joaquimrocha wants to merge 3 commits into
mainfrom
eng-233-wit-signed-fetch
Open

joaquimrocha wants to merge 3 commits into
mainfrom
eng-233-wit-signed-fetch

Conversation

@joaquimrocha

@joaquimrocha joaquimrocha commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

This PR allows quarry to fetch the repo data using WIMSE.

@joaquimrocha
joaquimrocha force-pushed the eng-233-wit-signed-fetch branch from 0afe91d to b08d284 Compare October 14, 2026 14:09
@joaquimrocha
joaquimrocha requested a review from cyphar October 14, 2026 15:51
@joaquimrocha
joaquimrocha force-pushed the eng-233-wit-signed-fetch branch 2 times, most recently from 4db8b8e to 5375f22 Compare October 19, 2026 08:41
Comment thread cmd/quarry-client/http.go
// request signatures, so targets of a repo with auth are proxied instead.
if info.Repo.Auth != nil {
return streamTargetFile(rw, req, info)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite unfortunate, though since only the method and URI are included in the signature I guess we can eventually pre-compute the signed headers and provide them to sd-pull once it switches to UAPI.16 (and a UAPI.16 extension is added to support it).

@davidstrauss Do we plan to extend the scope of things we would sign for WIMSE/WIT? If we start signing other headers then we would just need to accept that we are going to be proxying data a lot. (Though we could of course choose to proxy based n what headers are needed to be signed...)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might need more context, but I wasn't expecting we'd use WIT/WIMSE for repo auth at all. I thought we only wanted to rely on authZ mechanisms that (1) we can delegate to the delivery system itself (mainly for cost, DoS protection, and maybe some basic infrastructure disclosure control) or (2) we can achieve via sealing the data going through Quarry (for load-bearing secret delivery to specific machines).

In short, I assumed our entire model would work fine with publicly readable buckets.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can delegate to the delivery system itself (mainly for cost, DoS protection, and maybe some basic infrastructure disclosure control)

This is the "front door" access control we discussed in January, BTW.

Comment thread contrib/quarry-client/config.toml Outdated
## by a key quarry-client holds in memory and the token binds. Requests to
## other hosts (such as a separate data_root_url) are not signed. "wimse" is
## the only supported type.
#auth = { type = "wimse", audience = "urn:amutable:quarry-worker" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned when we first talked about this, links make this model a little complicated -- I think we would need to model this more like "providers" and then eventually we can come up with some kind of allow-list policy for providers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To flesh this out a bit more -- I think links would likely need to be able to provide something like "I want a provider with this rough shape of type + audience/user fields" and then the policy would determine whether a link can request such a provider and which one should be used.

The "shape" thing is needed because using provider identifiers is too fragile and allowing all of the bits of a provider to be configured is an obvious security issue (a hypothetical basic auth provider might support getting the user and password from a file and allowing a link to configure it would allow for local file leaks).

The simplest policy would be to only allow a repository to use providers that the linking repository has access to (which would walk back to the "root" set specified in configuration). That might be okay for the first version but that is not really the right model -- ideally you would also want the provider to specify which repositories are allowed to use the auth.

The WIT-signed headers include the whole request URI so for the WIMSE provider this restriction isn't strictly required but I'm just a little apprehensive about letting a repo specify what auth the client should send without any restrictions.

Comment thread internal/httputils/verified_http.go Resolved
@cyphar
cyphar force-pushed the eng-233-wit-signed-fetch branch 2 times, most recently from 4df5363 to 753e1af Compare October 26, 2026 08:30
@joaquimrocha

Copy link
Copy Markdown
Member Author

Haven't tested it here (it's a public holiday) but the changes LGTM.

@cyphar
cyphar force-pushed the eng-233-wit-signed-fetch branch 3 times, most recently from ab05b01 to b698797 Compare November 3, 2026 07:56
Comment thread internal/tufclient/client.go Outdated
Comment on lines +93 to +99
// Only a missing root.json means the repository may not exist
// yet. Any other failure (auth, network, server error) must not
// look like an empty repository list.
if errors.Is(err, fs.ErrNotExist) {
err = skippableErr(err)
}
return nil, err

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is actually correct -- if I configure a repository with broken auth (or a repository where the server hasn't yet allowed access to this client) then I think the behaviour should be the same as if I configured a repository that does not exist.

If the root_trust is configured to wait until the repository is accessible, I don't think 404s are less problematic than 403s. And again, this only applies to first-pull (and eventually this will not be used by AOS anyway).

I've moved this to a separate PR -- #95.

Callers that need to use a non-standard Client can now do so by passing
one, other callers can pass http.DefaultClient to maintain the existing
behaviour.

Co-developed-by: Joaquim Rocha <joaquim@amutable.com>
Signed-off-by: Joaquim Rocha <joaquim@amutable.com>
Signed-off-by: Aleksa Sarai <aleksa@amutable.com>
For now this is only needed for usage with WIMSE workload-to-workload
authenticated requests, so only the key bits for signing requuests are
implemented.

There are only a handful of implementations of RFC 9421 in Go and as far
as I can tell, they all have some kind of serious flaw that means we
cannot use them -- most explicitly require access to the raw private
key to sign messages rather than using crypto.Signer and most have
really unfortunate APIs.

Signed-off-by: Aleksa Sarai <aleksa@amutable.com>
A server that gives each machine only its own repo has to know which
machine is fetching.

A repo can now set an auth table of type "wimse" with an audience.
Requests to its metadata host then carry this machine's workload
identity token, from amutablectl's identity agent, and an RFC 9421
signature by a key quarry-client keeps in memory. Requests to other
hosts stay unsigned. Repos without the setting fetch as before.

Co-developed-by: Joaquim Rocha <joaquim@amutable.com>
Signed-off-by: Joaquim Rocha <joaquim@amutable.com>
Signed-off-by: Aleksa Sarai <aleksa@amutable.com>
@cyphar
cyphar force-pushed the eng-233-wit-signed-fetch branch from b698797 to 5b5b6c6 Compare November 3, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants