Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute an uncompressed digest for chunked layers #2155

Merged
merged 7 commits into from
Jan 9, 2025

Conversation

mtrmac
Copy link
Collaborator

@mtrmac mtrmac commented Oct 30, 2024

… to ensure we can always use traditional layer IDs and image IDs, and use the uncompressed digests to avoid zstd:chunked view ambiguity, in c/image.

Absolutely untested.

mtrmac added a commit to mtrmac/image that referenced this pull request Oct 30, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Oct 30, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Oct 30, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Oct 30, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
}
defer fg.Close()
digester := digest.Canonical.Digester()
if err := asm.WriteOutputTarStream(fg, metadata, digester.Hash()); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the expensive part where we're basically re-synthesizing the tarball from the unpacked files and the tar split-data just to get the checksum, right?

I know this is just a PoC but it seems very much worth a comment at the top of this conditional, something like:

// If we don't yet have the uncompressed digest, compute it now. This is needed
// for zstd:chunked for example to ensure callers identifying images by this
// have the data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the expensive part where we're basically re-synthesizing the tarball from the unpacked files and the tar split-data just to get the checksum, right?

Yes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Comment added, discussing more of the goals, costs and tradeoffs.

}
}

dirFD2, err := unix.Dup(dirFD)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't have O_CLOEXEC set

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This Dup is no longer necessary.

}
filename = path
}
pathFD, err := securejoin.OpenatInRoot(fg.rootDir, filename)
Copy link
Contributor

Choose a reason for hiding this comment

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

(A little surprising securejoin doesn't have a high level securejoin.OpenReader or something)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(We have an userns.secureOpen internally with two other callers — centralizing that, and proposing that upstream, does sound attractive.)

@@ -1734,3 +1754,63 @@ func validateChunkChecksum(chunk *internal.FileMetadata, root, path string, offs

return digester.Digest() == digest
}

func newStagedFileGetter(dirFD int, differOpts *graphdriver.DifferOptions, entriesWithOriginalNames []fileMetadata) (*stagedFileGetter, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know this codebase well but it seems surprising to me that we wouldn't have an equivalent of this function somewhere else, there has to already be code that's serializing zstd:chunked to tar in another place which has to do the same thing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is a getter in layers.go which doesn’t support the composefs digest mapping (in that case we fall back to a different expensive way to generate the diff, IIRC).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I do agree that having those FileGetter implementations scattered around the codebase suggest that a better design might be possible. OTOH the path/digest mapping is expensive to maintain in a ready-to-use form.

Copy link
Contributor

Choose a reason for hiding this comment

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

OTOH the path/digest mapping is expensive to maintain in a ready-to-use form.

On the composefs side that's basically what the composefs blob is (plus all inline metadata) and it's pretty cheap to read back a deserialized abstract tree from a composefs blob, and a big bonus is that because fsverity is enabled on that file, validation is automatic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

… yes, that’s a great point. There already is a special overlayFileGetter with composefs support, but it uses a temporary mount instead of reading the data directly.

It seems likely that reading the data in user-space would be better, and that could be reused here.

But that’s, also, not the focus of this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Cc: @giuseppe

Copy link
Contributor

Choose a reason for hiding this comment

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

It's also possible to deserialize a composefs in userspace via composefs-info dump; for the use case of turning "composefs into tar" that can be done without mounting it at a kernel level if desired. We have Rust code wrapping that in our crates. I suspect what would be helpful is to try to create a more extensive Go library for composefs separate from this project that fleshes things out like this.

Basically a cool thing about composefs is that it can be natively kernel mounted and provide full verity and things like that, but also because it's just metadata you can think of it much like a tar-split that can be handled in userspace too and efficiently merged etc.

Copy link
Collaborator Author

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

Updated:

  • Introduced composefs.RegularFilePathForValidatedDigest(d) to centralize the path mapping logic
  • Avoided the Dup() of the top-level directory file handle
  • Added more comments.

@giuseppe RFC. Still mostly untested, but closer to the final form, I think.

Comment on lines 1719 to 1801
// Also, layers without a tar-split (estargz layers and old zstd:chunked layers) can't produce an UncompressedDigest that
// matches the expected RootFS.DiffID; they will need to use a special TOC-based layer ID, and we need to somehow (??)
// ensure unambiguity - either by always falling back to full pulls, or by never falling back.
Copy link
Collaborator Author

@mtrmac mtrmac Nov 15, 2024

Choose a reason for hiding this comment

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

@giuseppe @cgwalters note this.

zstd:chunked layers have tar-split in the current format since b5413c2 , i.e. c/storage 1.54 == Podman 5.1.0.

I’m not 100% sure what to do here. I’m leaning towards outright refusing to partially pull estargz and old chunked layers (falling back to full pull), that is “clearly correct” and always possible. (Users who insist on always using partial pulls, e.g. for composefs, can rebuild their images.) Alternatively, we could always do a partial pull and refuse a fallback, but then we would need to break pulling those layers on other graph drivers (or, eventually, implement partial pulls for those graph drivers) — and we still would have the signing ambiguity.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

estargz and old zstd:chunked layers now fall back to full pulls by default, unless the user opts out of layer integrity enforcement.

mtrmac added a commit to mtrmac/image that referenced this pull request Nov 18, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 18, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 18, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 18, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 20, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 22, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 25, 2024
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 25, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 26, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 26, 2024
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 27, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Nov 28, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 28, 2024
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Nov 29, 2024
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Dec 9, 2024
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Copy link
Member

@giuseppe giuseppe left a comment

Choose a reason for hiding this comment

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

just a nit, LGTM

Copy link
Contributor

openshift-ci bot commented Jan 7, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: giuseppe, mtrmac

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

mtrmac added 7 commits January 7, 2025 16:50
We will start validating them.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
- Use a map of struct{} to save on storage
- Track the items by path, not by the digest; that's one byte more
  per entry, but it will allow us to abstract the path logic.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
... to centralize the composefs backing file layout design.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
... so that it happens
- before we start doing anything destructive
- only once (with no risk of defaults getting out of sync)
- in a single place

Ideally this should happen along with the initial parsing
of the config file; this is not that, but it is a minor step
in that direction.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
…dlers

We will add more, format-specific, logic to the decision.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
…sed digest

This will allow c/image to validate the uncompressed digest against the config's
RootFS.DiffID value (ensuring that the layer's contents are the same when pulled
via TOC and traditionally); and the uncompressed digest will be used as a layer ID,
ensuring users see the traditional layer and image IDs they are used to.

This doesn't work for layers without a tar-split (all estargz, and old zstd:chunked
layers); for those, we fall back to traditional pulls.

Alternatively, for EXTREMELY restricted use cases, add an
"insecure_allow_unpredictable_image_contents" option to storage.conf. This option
allows partial pulls of estargz and old zstd:chunked layers, and skips the costly
uncompressed digest computation. It is then up to the user to worry about
images where the tar representation and the TOC representation don't match,
and about unpredictable image IDs.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Comment on lines +129 to +130
It allows partial pulls of images without guaranteeing that "partial
pulls" and non-partial pulls both result in consistent image contents.
Copy link
Collaborator Author

@mtrmac mtrmac Jan 7, 2025

Choose a reason for hiding this comment

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

BTW note the phrasing: without guaranteeing consistency. I.e. this disables the computation of uncompressed digest on partial pulls.

But images are still required to set correct DiffIDs: If we know the uncompressed digest for some reason (e.g. from a non-partial pull of the same blob, when the annotations were missing or the registry did not support partial pulls), and we see that it does not match DiffIDs, we are going to fail any future partial pull of that layer

mtrmac added a commit to mtrmac/image that referenced this pull request Jan 7, 2025
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/libpod that referenced this pull request Jan 7, 2025
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
@giuseppe
Copy link
Member

giuseppe commented Jan 9, 2025

@rhatdan @cgwalters PTAL

@rhatdan
Copy link
Member

rhatdan commented Jan 9, 2025

/lgtm

@openshift-ci openshift-ci bot added the lgtm label Jan 9, 2025
@openshift-merge-bot openshift-merge-bot bot merged commit 896fea9 into containers:main Jan 9, 2025
20 checks passed
@mtrmac mtrmac deleted the wip-authentic branch January 9, 2025 20:15
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 9, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 10, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 14, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 14, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 15, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 16, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 21, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
mtrmac added a commit to mtrmac/image that referenced this pull request Jan 21, 2025
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants