-
Notifications
You must be signed in to change notification settings - Fork 28
[sha512] image: add configurable digest support #375
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
base: main
Are you sure you want to change the base?
Conversation
|
✅ A new PR has been created in buildah to vendor these changes: containers/buildah#6412 |
ebb29e3 to
ba27cc9
Compare
ba27cc9 to
cf2c428
Compare
cf2c428 to
63e8928
Compare
63e8928 to
6621c6e
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.
A fairly brief skim of just the changes, without carefully thinking about any individual use case.
(“Request changes” for the storage DiffID matching enforcement)
image/copy/single.go
Outdated
| } | ||
|
|
||
| return digest.Canonical.FromReader(stream) | ||
| // Use the configured digest algorithm |
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 think this kind of comment adds much value; ideally the .Get function would be named such the purpose of the value would be obvious. Compare the naming discussion in #374.)
| return digest.Canonical.FromReader(stream) | ||
| // Use the configured digest algorithm | ||
| algorithm := supportedDigests.Get() | ||
| return algorithm.FromReader(stream) |
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 works locally, but there’s the cachedDiffID code path where we use an older value. That probably needs to be adjusted, if we want the behavior to be predictable for users.
(This is relevant for schema1 only, and that is nowadays entirely disabled in Docker and the distribution/distribution registry, at least by default. Arguably interoperable support for sha512+schema1 is never going to happen … but, for us, it might be easier to generate sha512+schema1 and let it fail, than to have an ~undocumented exception where we ignore the configuration, or to specifically hard-code an error path and make it absolutely impossible to use such a setup.)
image/copy/single_test.go
Outdated
| // Save original algorithm and set SHA512 | ||
| originalAlgorithm := supportedDigests.Get() | ||
| defer func() { | ||
| err := supportedDigests.Set(originalAlgorithm) | ||
| require.NoError(t, err) | ||
| }() | ||
| err = supportedDigests.Set(digest.SHA512) | ||
| require.NoError(t, err) |
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.
Applies throughout: Overriding a global inside a test like this is fairly unclean (and somewhat verbose); e.g. it ~prevents running the tests in parallel, in a not-externally-obvious way.
(Leaving aside the question whether we should have a supportedDigest global), I think a much cleaner way to deal with global state of this kind is to parametrize the tested function with an explicit “digest” parameter; maybe adding that parameter to an existing function, maybe splitting the primary function into a tested function with an extra parameter + a trivial wrapper (compare the various …WithHomeDir functions in the codebase).
| computedDigest := digest.FromBytes(blob) | ||
| if computedDigest != m.m.ConfigDescriptor.Digest { | ||
| return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.ConfigDescriptor.Digest) | ||
| // Use the same algorithm as the expected digest |
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.
(Also not worth commenting unless it is a surprising decision in the circumstances.)
| return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.ConfigDescriptor.Digest) | ||
| // Use the same algorithm as the expected digest | ||
| expectedDigest := m.m.ConfigDescriptor.Digest | ||
| algorithm := expectedDigest.Algorithm() |
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.
AFAICS, in principle, m.ConfigDescriptor.Digest is not necessarily validated getting to this point.
expectedDigest.Algorithm can, given invalid inputs, panic.
| inputDigest: digest.Digest("sha512:uninspected-value"), | ||
| computesDigest: true, | ||
| expectedDigest: digest.Canonical.FromBytes(testData), | ||
| }, |
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 fair enough, but does not exercise the newly added code/case.)
image/storage/storage_dest.go
Outdated
| // If the algorithms don't match, try to recompute the diffID with the correct algorithm | ||
| if trusted.diffID.Algorithm() != untrustedDiffID.Algorithm() { | ||
| // Use the algorithm from the config's diffID to recompute the trusted diffID | ||
| configAlgorithm := untrustedDiffID.Algorithm() |
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 not recomputing anything.
image/storage/storage_dest.go
Outdated
| // For now, just log this case and allow it through since both are valid digests | ||
| logrus.Debugf("Layer %d diffID algorithm mismatch: trusted=%s, config=%s, allowing through", index, trusted.diffID.Algorithm(), configAlgorithm) |
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.
As mentioned previously, this is a critical security check and must not be just removed.
| } | ||
| for _, data := range imgOptions.BigData { | ||
| if err := s.imageRef.transport.store.SetImageBigData(img.ID, data.Key, data.Data, manifest.Digest); err != nil { | ||
| if err := s.imageRef.transport.store.SetImageBigData(img.ID, data.Key, data.Data, digestFunc); 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 not equivalent, manifest.Digest processes schema1 manifests specially.
image/storage/storage_transport.go
Outdated
| return err | ||
| } | ||
| // Try SHA512 (128 characters) | ||
| if len(id) == 128 { |
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 does not generalize. Do we need to think about including the algorithm in the ID? (Or, alternatively, having supported-digests promise that it will only add ≤1 algorithm for each length — not sustainable long-term.)
6621c6e to
621025c
Compare
621025c to
241f03e
Compare
- Replace hardcoded SHA256 with configurable digest algorithms using storage/pkg/supported-digests - Add centralized digest validation utilities in image/pkg/digestvalidation - Implement parameterized digest computation in image/copy/single.go - Rename DigestIfCanonicalUnknown to DigestIfConfiguredUnknown for clarity Signed-off-by: Lokesh Mandvekar <lsm5@redhat.com>
241f03e to
e2b0cc6
Compare
|
@mtrmac This is intended at not before podman v5.8, so not in an immediate rush. I'll be happy to fix the storage stuff before we go forward with this, if you prefer. |
Depends on #374 . This includes the commit from there, but maybe better to let that one go in first and then I can rebase this one.
(cursor assisted)