-
Notifications
You must be signed in to change notification settings - Fork 677
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
name-assertion: Add a name-assertion type
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>
- Loading branch information
Showing
6 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# Name assertions | ||
|
||
The bulk of OCI image content is transmitted via a [Merkle DAG][merkle] with [descriptor](descriptor.md) references. | ||
The location-addressable [refs](image-layout.md) provide convenient access into that DAG, but they introduce a degree of uncertainty. | ||
Publishers MAY add signed name-assertions to the DAG to provide consumers with a way of mitigating ref uncertainty. | ||
The meaning of the assertion is that the name [`name`](#payload) applies to the blob referenced by [`blob`](#payload). | ||
|
||
This section defines the `application/vnd.oci.name.assertion.v1` [media type](media-types.md). | ||
|
||
## Media type header and payload | ||
|
||
All assertions MUST begin with the [US-ASCII][rfc5234-b.2] representation of their [media type][rfc4288] followed a [CRLF][rfc5234-b.1]. | ||
The content after the header is the *payload*. | ||
|
||
## Payload | ||
|
||
The [payload](#media-type-header-and-payload) for `application/vnd.oci.name.assertion.v1` MUST consist of [UTF-8][] [JSON][] object with the following properties: | ||
|
||
* **`name`** (string, REQUIRED) the name being asserted. | ||
* **`blob`** ([descriptor](descriptor.md), REQUIRED) the blob being named. | ||
|
||
There is a JSON Schema for this payload at [`schema/name-assertion-schema.json`](schema/name-assertion-schema.json) and a Go type at [`specs-go/v1/assertions.go`](specs-go/v1/assertions.go). | ||
|
||
## Example | ||
|
||
The name assertion: | ||
|
||
```title=Name%20assertion&mediatype=application/vnd.oci.name.assertion.v1 | ||
application/vnd.oci.name.assertion.v1 | ||
{ | ||
"name": "foo-bar v1.0", | ||
"blob": { | ||
"size": 4096, | ||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f", | ||
"mediatype": "application/vnd.oci.image.manifest.list.v1+json" | ||
} | ||
} | ||
``` | ||
|
||
asserts that [`sha256:e692…`](descriptor.md#digests-and-verification) is a 4096-byte [manifest list](manifest-list.md) named `foo-bar v1.0`. | ||
|
||
## Use | ||
|
||
You can use name assertions to name arbitrary blobs, but there are pros and cons to the various OCI [media types](media-types.md). | ||
|
||
### Manifest lists | ||
|
||
Naming a [manifest lists](manifest-list.md) locks in the full OCI-defined DAG. | ||
The manifest list's platform information ([`architecture`, `os`, `os.version`, `os.features`, etc.][manifest-list-properties]) route consumers to their appropriate manifest and are more detailed than platform information further down the DAG (the only [configuration](config.md) properties are [`architecture` and `os`](config.md#properties)). | ||
A malicious intermediate who alters a manifest list can disrupt the manifest-lookup process. | ||
|
||
Even with signed name assertions on the referenced [manifest](manifest.md) or [configuration](config.md), the unsigned manifest list allows the intermediate to deny service (e.g. by removing all the manifest entries) or by misdirecting lookups (e.g. by altering platform fields on existing entries). | ||
When the intermediate is only altering the manifest-list-specific platform information (e.g. [`features`][manifest-list-properties]), the change may not be detected during unpacking. | ||
|
||
Directing consumers at specially-crafted blobs may also exploit vulnerabilities in the consumer's blob-handling logic, much like [decompression bombs][decompression-bomb] and [tarbombs][]). | ||
Consumers who do not expect gzipped or tar-based media types in `manifests` can limit their exposure by restricting the set of supported media types for referenced blobs. | ||
Consumers who restrict their support for referenced-blob media types to [UTF-8][] [JSON][] are still vulnerable to attacks on their [UTF-8][]- and [JSON][]-parsing implementation, although they were already exposed to those attacks when they parsed the manifest list itself. | ||
|
||
[`annotations`][manifest-list-properties] is weakly structured, so the impact of malicious alterations is hard to assess. | ||
|
||
Signed name assertions on the manifest list allow consumers to avoid these disruptions. | ||
|
||
However, signing name assertions on manifest lists may require coordination among several parties (e.g. the developer producing the amd64 Linux version and the build farm producing the arm Linux version), and the original signature will not apply to new manifests resulting from any change to the downstream DAG. | ||
As an assertion on the highest-level OCI type, manifest list assertions are the most affected by these issues. | ||
|
||
### Manifests | ||
|
||
Naming a [manifest](manifest.md) locks in a single platform's DAG. | ||
|
||
A malicious intermediate who can perform the same lookup misdirection and reference bombing with [`config` and `layers`][manifest-properties] that the [manifest list attacker](#manifest-lists) could perform on `manifests`. | ||
`layers` alterations has a higher exposure to [decompression bombs][decompression-bomb] and [tarbombs][] because consumers will expect layers to be [gzipped tar archives](layer.md#distribution-format). | ||
With a signed name assertion on the referenced [configuration](configuration.md), the consumer can use the configuration's [`rootfs.diff_ids`](config.md#properties) to detect tar alteration (although that will not protect you from decompression bombs). | ||
|
||
[`annotations`][manifest-properties] is weakly structured, so the impact of malicious alterations is hard to assess. | ||
|
||
Signed name assertions on the manifest allow consumers to avoid these disruptions. | ||
|
||
However, signing manifests may require coordination among several parties (e.g. the original publisher and the CAS admin who is replacing equivalent layers with a [consistently-compressed version](canonicalization.md)). | ||
As an assertion on a mid-level OCI type, manifest assertions are moderately affected by these issues. | ||
|
||
### Configurations | ||
|
||
Naming a [configuration](configuration.md) locks in a single platform's unpacked filesystem. | ||
|
||
With a signed name assertion on the [configuration](configuration.md), the consumer can be sure that a successfully unpacked filesystem contains an image of the asserted name. | ||
And except for the [manifest-list-specific platform fields](#manifest-lists), the consumer can be sure that the unpacked filesystem matches the intended platform. | ||
Without a signed name assertion on the configuration or the DAG leading to it, a malicious intermediate may have complete control over the unpacked filesystem. | ||
|
||
Signing name assertions on the configuration rarely requires coordination among several parties. | ||
As an assertion on a low-level OCI type, configuration assertions are minimally affected by these issues. | ||
|
||
### Layers | ||
|
||
Naming a [layer](layer.md) locks in the blob, but there are no references to other blobs (not even the configuration's quasi-reference [`rootfs.diff_ids`](config.md#properties)). | ||
Signing assertions on layers directly may be useful for image assemblers in frameworks that use layers as a package-management system, with package managers publishing layers with signed name assertions and integrators assembling those layers into images. | ||
However, the asserted name will rarely match the image-consumer's requested name (e.g. requesting an `nginx-1.10.1` image might pull in layers for `glibc-2.22` and `zlib-1.2.8`). | ||
|
||
Signing name assertions on layers may require coordination among several parties (e.g. the original publisher and the CAS admin who is replacing equivalent layers with a [consistently-compressed version](canonicalization.md)). | ||
As an assertion on a low-level OCI type whose stability may be relaxed by `rootfs.diff_ids`, layer assertions are moderately affected by these issues. | ||
|
||
[JSON]: https://tools.ietf.org/html/rfc7159 | ||
[decompression-bombs]: https://en.wikipedia.org/wiki/Zip_bomb | ||
[tarbombs]: https://en.wikipedia.org/wiki/Tar_%28computing%29#Tarbomb | ||
[manifest-list-properties]: manifest-list.md#manifest-list-property-descriptions | ||
[manifest-properties]: manifest.md#image-manifest-property-descriptions | ||
[merkle]: https://en.wikipedia.org/wiki/Merkle_tree | ||
[rfc4288]: https://tools.ietf.org/html/rfc4288 | ||
[rfc5234-b.1]: https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
[rfc5234-b.2]: https://tools.ietf.org/html/rfc5234#appendix-B.2 | ||
[UTF-8]: http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"description": "Open Container Initiative Name Assertion Specification", | ||
"$schema": "http://json-schema.org/draft-04/schema#", | ||
"id": "https://opencontainers.org/schema/image/name-assertion", | ||
"type": "object", | ||
"properties": { | ||
"name": { | ||
"description": "The name being asserted.", | ||
"id": "https://opencontainers.org/schema/image/name-assertion/name", | ||
"type": "string" | ||
}, | ||
"blob": { | ||
"id": "https://opencontainers.org/schema/image/name-assertion/blob", | ||
"$ref": "content-descriptor.json" | ||
} | ||
}, | ||
"required": [ | ||
"name", | ||
"blob" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright 2016 The Linux Foundation | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1 | ||
|
||
// NameAssertion defines a name assertion payload. | ||
type NameAssertion struct { | ||
// Name is the name being asserted. | ||
Name string `json:"name"` | ||
|
||
// Blob is the blob being named. | ||
Blob Descriptor `json:"blob"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters