CNAB (Cloud Native Application Bundles) is a package format specification that describes a technology for bundling, installing, and managing distributed applications, that are by design, cloud agnostic. Specifically, it uses container images to incapsulate installation logic and all runtime dependencies required to deploy a distributed application.
According to version 1.0 of the CNAB Core specification, a bundle consists of:
Bundles have two canonical representations:
In order for bundles to be usable, they have to be distributed. And since a bundle represents a collection of metadata and container images that are needed for an application, one way of distributing them is by using OCI registries - by representing a thin bundle as an OCI index through cnab-to-oci
, the implementation for distributing bundles through OCI registries.
(This article talks about the current state of the implementation, and this article examines the requirements needed for distributing bundles with OCI registries).
Image from the blog post announcing the donation of
cnab-to-oci
to the CNAB community by Docker.
The CNAB Security specification proposes the use of TUF to ensure the authenticity and integrity of bundles distributed between registries and clients.
The specification does not prescribe or restrict the use of a specific TUF implementation, but it does prescribe the way metadata should be stored in a TUF collection, and the implementation uses the current Notary client libraries to push bundle metadata to a Notary server.
When pushing a bundle to an OCI registry, clients will:
$ sha256sum testdata/cnab/bundle.json
c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 testdata/cnab/bundle.json
$ signy
--tlscacert=$NOTARY_CA
--server https://localhost:4443
sign testdata/cnab/bundle.json localhost:5000/thin-bundle:v1
INFO[0000] Starting to copy image cnab/helloworld:0.1.1
INFO[0002] Completed image cnab/helloworld:0.1.1 copy
INFO[0002] Generated relocation map: relocation.ImageRelocationMap{"cnab/helloworld:0.1.1":"localhost:5000/thin-bundle@sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6"}
INFO[0002] Pushed successfully, with digest "sha256:b4936e42304c184bafc9b06dde9ea1f979129e09a021a8f40abc07f736de9268"
INFO[0000] Pushed trust data for localhost:5000/thin-bundle:v1: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5
$ signy
--tlscacert=$NOTARY_CA
--server https://localhost:4443
verify localhost:5000/thin-bundle:v1
INFO[0000] Pulled trust data for localhost:5000/thin-bundle:v1, with role targets - SHA256: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5
INFO[0000] Pulling bundle from registry: localhost:5000/thin-bundle:v1
INFO[0000] Computed SHA: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5
INFO[0000] The SHA sums are equal: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5
Users can choose to distribute bundles without OCI registries.
This means that the object we apply the signature to cannot be dependent on a a particular representation of that content - which is the case for the current signature model for OCI artifacts (signing the content digest of the manifest).
So the object we are signing is bundle.json
- below is a subset of a bundle.json
that describes two images used:
{
"images": {
"backend": {
"contentDigest": "sha256:bca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120686",
"description": "backend component image",
"image": "example.com/example/vote-backend@sha256:bca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120686",
"imageType": "docker"
},
"frontend": {
"contentDigest": "sha256:aca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120685",
"description": "frontend component image",
"image": "example.com/example/vote-frontend@sha256:aca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120685",
"imageType": "docker"
}
}
}
CNAB also defines a process for relocation - where the bundle and all referenced images are moved from one registry to another.
This process can be performed by also maintaining any original bundle signature, with the requirement that the new registry yields the same content digest for the images referenced in the bundle.
There are scenarios where users want to distribute bundles without OCI registries - either because they have an existing method for distributing artifacts they want to reuse, or they want a thick bundle representation they want to distribute in air-gapped environments.
In either case, they should be able to reuse the same signing model. Particularly, this generates the following requirements:
We currently use the custom
field in a TUF collection to distribute in-toto metadata associated with a bundle, process derived from this in-toto enhancement proposal for TUF interoperability.
CNAB Security needs to be able to support all of the following requirements:
Unfortunately, Notary v1 has a few limitations that prevents CNAB Security from supporting the requirements listed above:
root
and targets
keys per bundle (because there is a separate TUF metadata repository per bundle).timestamp
and snapshot
keys per bundle.There are five design principles for key management in TUF that we believe would be useful in a wide variety of use cases in Notary v2.
The first principle is the separation of duties, or using different sets of keys to sign different sets of metadata. The notion of a role, whether man or machine, is used to distinguish between different sets of keys. This ensures that a compromise of a set of keys belonging to any one role is generally insufficient to compromise the security of the entire system.
Notary v1 has already implemented this principle.
The second principle is allowing for threshold signatures, or m out of n keys, to sign any piece of metadata. This is so that a compromise of less than m keys is insufficient to cause false metadata to be signed.
Notary v1 has already implemented this principle.
The third principle is allowing for using a diversity of cryptographic hashing and signing algorithms at the same time. This is so that a compromise of any signing or hashing algorithm is insufficient to cause false metadata to be trusted.
Notary v1 has not implemented this principle. Signatures are limited to P-384, and hashes limited to SHA2-256.
The fourth principle is building key rotation and revocation into the system. Keys will inevitably be lost or somehow compromised (e.g., reverse-engineered or stolen), and so there needs to be a way to revoke these keys and replace them with new ones. There are two ways to revoke keys in TUF: explicitly (using old keys to sign for new keys in new pieces of metadata) and implicitly (setting expiration timestamps on pieces of metadata so that keys are not necessarily trusted indefinitely).
Notary v1 has already implemented this principle.
Last but not least, the fifth principle is not usually explicitly discussed, but is nevertheless crucial to the scaling of key distribution, rotation, and revocation in the system.
The use of delegations is a powerful strategy that has successfully been used in a variety of contexts, including distributed systems, role-based access control, and software repositories. In the context of software repositories, delegations are specifically used to distribute permissions to sign packages across different administrators and developers. If A can sign a package K, then A can delegate this permission to B so that B can sign K on behalf of A. The delegation is an indirect package signature, where B "speaks for" A about K.
Consider the following example discussed in the Diplomat paper. In the figure above, there is a "projects" role may sign packages because it is the root of trust for all packages. However, it has instead delegated the Django project (or the package path Django-*
) to the public keys belonging to the developer Alice. Similarly, the Scapy project has been delegated to Sue. A delegation is simply a trusted map of which developer keys are responsible for signing which projects (or sets of packages). Based on this delegation, users would trust only Alice’s signature on a Django package. Developers can further delegate entrusted packages to other developers. In this case, Alice has delegated some packages (any package matching the path Django-*.tar.gz
) to the developer Bob. Thus, Bob speaks for Alice for only the Django-*.tar.gz
packages, whereas Alice’s signature on Django-1.7.1.exe
(not shown) would be trusted instead of Bob’s.
Although Notary v1 has implemented some notion of delegations, it has unfortunately not implemented it in a way that matches the TUF semantics. Without going into technical details, the semantics of delegations in Notary v1 is not rich enough to capture the semantics of delegations in TUF which are needed for some security use cases, such as the one discussed above for CNAB.