sigstore / sig-clients

Home of the clients SIG
Apache License 2.0
5 stars 4 forks source link

Trusting multiple TUF repositories #11

Open haydentherapper opened 11 months ago

haydentherapper commented 11 months ago

Description

I've been thinking about private deployments in which consumers will trust artifacts signed in both their private environment and from the public good instance. Currently, clients are configured to trust a single TUF repository. The UX is not ideal in that clients will have to reinitialize their TUF metadata before verifying artifacts signed with each infrastructure. Furthermore, if an artifact relies on multiple TUF environments to be verified, it is not currently possible (I believe, let me know if I'm wrong) to verify the artifact. This could occur for example if an artifact is signed with public Fulcio and recorded to public Rekor, but uses a private timestamp authority.

What should the user experience look like in this case? For Cosign, TUF metadata is either trusted explicitly with an initialize command, or on-demand assuming the TUF metadata can be verified with the baked-in TUF root. I'm not sure how other clients allow custom roots to be specified. I would propose explicit initialization with multiple trusted TUF roots. Maybe an option to say "trust this set of roots + the baked-in root"?

How should verification handle targets across multiple TUF repos? Should a client explicitly state if they want to be able to verify using targets across both (like the example case I gave)? Or should it be implicit after initialization with multiple TUF roots?

Any other concerns to think through?

cc @kommendorkapten @woodruffw @loosebazooka @bdehamer @jku @steiza for thoughts

mnm678 commented 11 months ago

I'd recommend taking a look at TAP 4. The automotive variant of TUF uses two TUF repositories, with consensus between them before updates are installed. The scenario here is a bit different, where different responsibilities need to be assigned to each TUF repository. The map file proposed in TAP 4 also allows for this type of assignment to different repositories. Basically the map file could say: use the public TUF root for Fulcio and the private root for timestamping.

loosebazooka commented 11 months ago

We use the trusted-root.json to define what the "current" rekor and fulcio instances are for signing. As long as we can define "current", we should be good. Otherwise we probably have to re-define how we select infrastructure at signing.

For verification I think "current" matters less, although you might want to define boundaries to prevent some sort of squatting that might occur when conflicts happen between internal and external repositories? (defining these boundaries is somewhat susceptible to human error)

steiza commented 11 months ago

The TUF repository here is sort of a proxy for "how do I get the trusted_root.json to verify this artifact?"

I think clients should use the narrowest possible trusted_root.json to verify an artifact. If a system includes a mix of things signed with public good infrastructure and private infrastructure, the system should keep track of which is which so it can provide the narrowest possible information to the verifier.

Clients should support mutually exclusive options to:

Most of the time, people will default to public good (or the TUF repository for their private deployment). If they don't have a TUF repository for their private deployment, or if they have some weird hybrid case, they can construct their own trusted_root.json.

For the proposed use-case of public good plus a private timestamp authority, they can use their own TUF client to get the public good trusted_root.json, get the information for their private timestamp authority however they'd like, and construct a custom trusted_root.json to then supply to the verifier.

haydentherapper commented 11 months ago

That's reasonable to recommend constructing their own trusted_root.json. I do think that would be a barrier to entry though, as it would require maintaining their own TUF repository to ship trusted_root.json and handling root signing key management.

@mnm678 I've seen TAP4 before, I think that would be great to explore here.

That's a good point around selecting "current" for signing, you'd need some UX around selecting which instance is trusted for signing. This issue really only makes sense for the verification flows now that I think about it.

kommendorkapten commented 11 months ago

Last summer we did look into TAP-4, but we never went ahead and implement anything. See this doc

One caveat with TAP-4 used in this way is that shadowing can occur (I believe TAP-4 is mostly used to gather consensus between repositories, or using different repositories for different purposes as @mnm678 mentions).

If two repositories "public good" and "internal" both exposes a target trusted_root.json, that target can only be retrieved from one repository, the search order is specified in the file map.json. To circumvent this, one could require (globally) unique naming of the files, which may be hard in practice.

All in all I agree with @steiza here. I believe a user has to be explicit about what they are trusting when verifying.

Specifying a TUF repository (default to public good)

Even without TAP-4 a client could be aware of multiple TUF repositories, and via a flag/parameter chose which one to use. I think this is a simple solution (just be aware that for caching of metadata (and possibly targets) has to be separated, e.g. each metadata in a directory named by the FQDN to the repo or so.

trishankatdatadog commented 11 months ago

Interesting issue. All in all, I will say that whatever solution is chosen here, it should avoid the usual footguns (e.g., dependency confusion). Perhaps the best place to begin is by eliciting and enumerating use cases 🙂

jku commented 11 months ago

I think it is useful for this discussion to happen without any reference to TUF -- I believe talking about specific TAPs etc railroads the discussion in harmful ways.

Assuming trusted_root.json defines the source of truth for a client: