-
Notifications
You must be signed in to change notification settings - Fork 677
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
name-assertion: Add a name-assertion type #445
Conversation
fbcb1fe
to
05c24bc
Compare
05c24bc
to
323dc2a
Compare
I very strongly feel that, for image signing, this is undesirable and there shouldn’t be any user-visible “name assertion” concept or file format. Yes, a “name assertion” may be an useful concept to use when thinking about some particular signature implementations, but ultimately we are not in the algebra-like business of boiling down concepts to their purest and most abstract forms; we are in the engineering business of designing a robust system of file formats. One of the concerns about signature security is type/content/semantics confusion: What if an attacker is able to take a signature of a layer and convince a consumer that it is a signature of an image, or the other way around? What if an attacker is able to take a signature naming an URL and convince a consumer that is a signature of a Docker reference, or vice versa? Creating a generic “name assertion” format which is intentionally substitutable in both of these ways would create completely unnecessary opportunities for such confusion. AFAICT we should be doing the exact opposite, and defining signature formats such that it is, as far as possible, entirely unconcievable to apply a signature to the wrong kind of object or the wrong kind of semantics. Preferably there should be only one kind of signature; and if there are several, they should be stored by the storage mechanism / distributed by the distribution mechanism in completely separate locations which are never mixed together, and they should use formats which are clearly not interoperable (if there were hypothethical image signatures and manifest signatures, we should ensure that a layer signature which contains a manifest digest, or a manifest signature which contains a layer ID, would always be reliably rejected). From my POV, it is no more practical to talk about abstract “name assertions” instead of “image signatures” at the file format level than it is appropriate to talk about abstract “finite fields” instead of the need for particular side-channel- free "Z_n" big-endian bignum implementations at the RSA implementation level. Sure, these concepts can be very useful when thinking about the design and implementation, and perhaps as an internal helper inside the implementation (though relying on a strong type system separation between different kinds of signatures which never meet in the code seems more robust). But these algebra-like concepts should really not leak into the user-visible file formats, especially not as a substitutable layer of abstraction. |
On Fri, Nov 04, 2016 at 02:06:14PM -0700, Miloslav Trmač wrote:
Are you having display concerns here? Clearly, if you've signed a
I've tried to motivate naming each existing OCI type in my topic post.
Inventing separate formats and distribution mechanisms for multiple
I'm having difficulty parsing this. Are you saying someone signs: application/vnd.oci.name.assertion.v1 to name a layer, and then an attacker provides the original signature, Or are you saying they forge their own name assertion like: application/vnd.oci.name.assertion.v1 and provide the original signature and the forged name assertion Both of those seem like far-fetched hash-collision attacks to me. If |
In short, signing individual layers has dubious value to me (especially try forming a 5-minute elevator pitch which explains the user what a layer signature exactly does or does not mean), and we shouldn’t be asking the user to make any decision between signing configs/arch-specific manifest/manifest digests; it is for us to design and choose the best one, or the best combination. (We are not creating Legos from which anyone can individually build their own special cryptosystem snowflake; we are creating an interoperability specification, we need every consumer of the spec to interpret the signatures consistently. Options/variants make systems less interoperable, and more confusing to the user. (Also, for creating an interoperability spec, a fair degree of the semantics of the signatures is essential component of the interoperability spec, for the same reason.))
Really I’m betting on “there should be only one”; but, if nothing else, using entirely specific field names, like
The base problematic scenario is:
Of course the $somehow is the first failure point, but there really are distinct layer and manifest signatures, they pretty much must have distinct semantics, and this attack allows escalating an unauthorized layer signature into an unauthorized manifest signature. |
(And this is much worse for the |
On Fri, Nov 04, 2016 at 02:53:00PM -0700, Miloslav Trmač wrote:
I think the “I'm compiling an image from single-package layers that
I disagree. I think it's good to provide the tools in one layer, and
I agree that diverging crypto policies will make the whole ecosystem
Absolutely, and I think that the current semantics are pretty clear. “Down to the manifest and still no signed name assertion? I'm Once they find a signed name assertion they verify it: “Ah, Alice signed this. She knows what she's doing. Lets open the or maybe: … Looks like she thought the next step in the DAG was a manifest or maybe: “Charlie? Who is Charlie? I'm getting out of here!” etc. And namers get to decide how much resigning their is willing to “Ah, you've replaced my sha256:abc… layer with a sha256:123… layer or: “I'm never going to touch this project again. Better sign the
The media type header means you can't even unmarshal the assertion
Your argument here seems to be:
I'm fine with (1), but don't buy (2). Respecting a descriptor's If we did go to (3), we'd be pulling in new, non-core descriptor-ish On Fri, Nov 04, 2016 at 02:54:52PM -0700, Miloslav Trmač wrote:
The semantics are available via assertion's media type → spec defining “It is a little odd that the ‘name’ value must always be That doesn't seem very likely to me. |
323dc2a
to
052646e
Compare
This doesn't get into signature formats, assertion discovery, or signature-discovery. But we need to agree on the assertion format itself before those become interesting problems. We can continue to fill in the remaining pieces in follow-up work. The assertion blob must be self-describing because the signature covers the blob itself, and not any descriptors referencing that blob. For example, a linking structure like: { "blob": {descriptor referencing the assertion}, "signature": {descriptor referencing the signature} } might be altered by an attacker to adjust the descriptor media types. Having self-describing assertions and signature blobs protects from such attacks. The media-type header makes assertions self-describing in a way that can survive potential future transitions to non-JSON formats. If we are comfortable requiring JSON assertions forever, we can move the self-describing media type into the assertion object as mediaType. The media-type header is awkward enough that it's good to limit it to the small assertion blob. An alternative approach to applying assertions would be to add the asserted attribute (e.g. the name) to the signed object itself (e.g. as a manifest property), but having purely-JSON manifests (without the media-type header) makes them easier to manipulate with generic tools like jq. Having the assertion in an independent object also clarifies the assertion being signed. With a 'name' property on the manifest, a signature on the manifest is signing the entire DAG rooted at that manifest and asserting something vaguely good about that DAG. With a separate name assertion you can say "this is Nginx 1.10.1" without implying "I've audited all the source code and the build system for security flaws and found none" or other similarly high bars. You can of course define a separate security-audit assertion and apply that to the manifest as well. A final benefit of stand-alone assertions is that you can apply them to any blob. The name assertion added hear can be applied to manifest-lists, manifests, configurations, layers, etc., etc. Getting the same coverage via embedded properties would require per-type adjustments. Signed-off-by: W. Trevor King <wking@tremily.us>
052646e
to
3aae681
Compare
Considering that the only way to refer to layers is by digests, which are already authenticating them, keeping the layer digests in a build system database, and keeping the layer digests + a set of public key in the same build system database is not noticeably more secure. Compromising that database allows an attacker to substitute a layer either way. Unless you are proposing some kind of repo/name/tagging system for individual layers which would allow for reasonable end-to-end verification.
There are already enough GPG and CMS and whatever signing implementations and enough data formats around that everyone has more than enough Lego bricks to built whatever they need. OCI should be the place where the “hashing it out” happens so that the whole ecosystem is interoperable (who else would do it? why not OCI?). If the result of the signing effort is not an interoperable specification and we end up requiring every pair of users to "hash it out” then we have really achieved nothing of value to me.
What is a name? It’s just a string. “Applying a string to a blob” has close to zero semantics.
What about content of any other files which have already been processed? What if the ref was using a
No, that only assures the client that it is processing an assertion; not an assertion for the expected kind of object and name.
No, I don’t think we do need them; but name assertions as an abstraction only make sense if there are distinct kinds of signatures, so the attack scenario assumes that.
No, so far it has been very easy to ignore it; I know that manifest lists should contain manifests, and I know that manifests refer to blobs. Why would I even look at the manifest MIME type?
That’s really backwards; we want to minimize the amount of code which needs to be paranoid. The signature verification should happen as early in the process as possible, with as minimal handling of the contents of the signed data as possible. A fully generic “sane” DAG-walking implementation should not be even allowed to touch the data until it has been cryptographically verified.
(You’ve suggested elsewhere that signatures should reuse the |
On Mon, Nov 07, 2016 at 08:14:27AM -0800, Miloslav Trmač wrote:
Having a digest referencing a layer does not necessarily mean that a. The layer is named, or On the other hand, if you follow a ref to a signed name assertion, and
Adding public keys to CAS does not affect security (and I'm not
This PR is currently fuzzy about how signatures are stored or
We might setup in-DAG naming (e.g. manifest-list → name assertion →
GnuPG allows you to sign things, but doesn't care about what you're However, while a number of folks have called for “what should we tell
A deep question. That which we call a rose… ;).
It's an identity document, and there are lots of other examples of
They are obviously not covered by the assertion.
Policy call, but personally I'd bail.
I don't see a point to putting port numbers in the names. Where you
I think you're still conflating “where the ref engine lives” and “what
After parsing the media type header you know you're processing an
A separate name-assertion blob assumes either of: a. There are multiple types of things you'd like to name (this is what
Manifests refer to layers. What are the semantics of those layers?
I'm not saying “respecting a blob's mediaType is paranoid”, I'm saying
I don't think that restrictive policy is going to be universal, so I'd ref name You still have to somehow get from the ref to the name assertion and I don't see the content of the assertion blob as a large bomb risk in
I think assertions should reuse the descriptor type to reference the
The code verifying the signatures should have already been written (in
I'm fine having ‘urls’ in the assertion (which is parsed after |
This PR doesn't incorporate a hint of understanding of the issues identified in #400. I'm closing until that conversation concludes with real consensus from maintainers. |
This doesn't get into signature formats, assertion discovery, or signature-discovery. But we need to agree on the assertion format itself before those become interesting problems. We can continue to fill in the remaining pieces in follow-up work.
The assertion blob must be self-describing because the signature covers the blob itself, and not any descriptors referencing that blob. For example, a linking structure like:
might be altered by an attacker to adjust the descriptor media types. Having self-describing assertions and signature blobs protects from such attacks.
The media-type header makes assertions self-describing in a way that can survive potential future transitions to non-JSON formats. If we are comfortable requiring JSON assertions forever, we can move the self-describing media type into the assertion object as mediaType.
The media-type header is awkward enough that it's good to limit it to the small assertion blob. An alternative approach to applying assertions would be to add the asserted attribute (e.g. the name) to the signed object itself (e.g. as a manifest property), but having purely-JSON manifests (without the media-type header) makes them easier to manipulate with generic tools like jq.
Having the assertion in an independent object also clarifies the assertion being signed. With a
name
property on the manifest, a signature on the manifest is signing the entire DAG rooted at that manifest and asserting something vaguely good about that DAG. With a separate name assertion you can say “this is Nginx 1.10.1” without implying “I've audited all the source code and the build system for security flaws and found none” or other similarly high bars. You can of course define a separate security-audit assertion and apply that to the manifest as well.A final benefit of stand-alone assertions is that you can apply them to any blob. The name assertion added hear can be applied to manifest-lists, manifests, configurations, layers, etc., etc. Getting the same coverage via embedded properties would require per-type adjustments.
Related discussion in #22, #173, #176, #400, and containers/image#59.