-
Notifications
You must be signed in to change notification settings - Fork 247
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
WIP: Compute an uncompressed digest for chunked layers #2155
base: main
Are you sure you want to change the base?
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: 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 |
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
pkg/chunked/storage_linux.go
Outdated
} | ||
defer fg.Close() | ||
digester := digest.Canonical.Digester() | ||
if err := asm.WriteOutputTarStream(fg, metadata, digester.Hash()); err != nil { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
pkg/chunked/storage_linux.go
Outdated
} | ||
} | ||
|
||
dirFD2, err := unix.Dup(dirFD) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.)
pkg/chunked/storage_linux.go
Outdated
@@ -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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cc: @giuseppe
There was a problem hiding this comment.
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.
043e2cd
to
4c9e6d8
Compare
There was a problem hiding this 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.
pkg/chunked/storage_linux.go
Outdated
// 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
4c9e6d8
to
498b37f
Compare
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
changes LGTM Anything more you'd like to do before it is ready for review/merge? |
This could be merged as is, but the plan of record is to add an opt-out option. But, most importantly, I want to test that this, along with containers/image#2613 , actually works. |
88a5e7b
to
2be75fc
Compare
Updated:
Still untested ATM. @giuseppe PTAL for an early review. |
63d31d3
to
cee8ecf
Compare
|
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
cee8ecf
to
bc093e6
Compare
We will start validating them. Signed-off-by: Miloslav Trmač <[email protected]>
- 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č <[email protected]>
... to centralize the composefs backing file layout design. Signed-off-by: Miloslav Trmač <[email protected]>
... 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č <[email protected]>
Should not change behavior. Signed-off-by: Miloslav Trmač <[email protected]>
…dlers We will add more, format-specific, logic to the decision. Should not change behavior. Signed-off-by: Miloslav Trmač <[email protected]>
…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 representaiton and the TOC representation don't match, and about unpredictable image IDs. Signed-off-by: Miloslav Trmač <[email protected]>
bc093e6
to
834e871
Compare
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
> go mod edit -replace github.com/containers/storage=github.com/mtrmac/storage@wip-authentic Signed-off-by: Miloslav Trmač <[email protected]>
… 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.