|
| 1 | +# Signature access protocols |
| 2 | + |
| 3 | +The `github.com/containers/image` library supports signatures implemented as blobs “attached to” an image. |
| 4 | +Some image transports (local storage formats and remote procotocols) implement these signatures natively |
| 5 | +or trivially; for others, the protocol extensions described below are necessary. |
| 6 | + |
| 7 | +## docker/distribution registries—separate storage |
| 8 | + |
| 9 | +### Usage |
| 10 | + |
| 11 | +Any existing docker/distribution registry, whether or not it natively supports signatures, |
| 12 | +can be augmented with separate signature storage by configuring a signature storage URL in [`registries.d`](registries.d.md). |
| 13 | +`registries.d` can be configured to use one storage URL for a whole docker/distribution server, |
| 14 | +or also separate URLs for smaller namespaces or individual repositories within the server |
| 15 | +(which e.g. allows image authors to manage their own signature storage while publishing |
| 16 | +the images on the public `docker.io` server). |
| 17 | + |
| 18 | +The signature storage URL defines a root of a path hierarchy. |
| 19 | +It can be either a `file:///…` URL, pointing to a local directory structure, |
| 20 | +or a `http`/`https` URL, pointing to a remote server. |
| 21 | +`file:///` signature storage can be both read and written, `http`/`https` only supports reading. |
| 22 | + |
| 23 | +The same path hierarchy is used in both cases, so the HTTP/HTTPS server can be |
| 24 | +a simple static web server serving a directory structure created by writing to a `file:///` signature storage. |
| 25 | +(This of course does not prevent other server implementations, |
| 26 | +e.g. a HTTP server reading signatures from a database.) |
| 27 | + |
| 28 | +The usual workflow for producing and distributing images using the separate storage mechanism |
| 29 | +is to configure the repository in `registries.d` with `sigstore-staging` URL pointing to a private |
| 30 | +`file:///` staging area, and a `sigstore` URL pointing to a public web server. |
| 31 | +To publish an image, the image author would sign the image as necessary (e.g. using `skopeo copy`), |
| 32 | +and then copy the created directory structure from the `file:///` staging area |
| 33 | +to a subdirectory of a webroot of the public web server so that they are accessible using the public `sigstore` URL. |
| 34 | +The author would also instruct consumers of the image to, or provide a `registries.d` configuration file to, |
| 35 | +set up a `sigstore` URL pointing to the public web server. |
| 36 | + |
| 37 | +### Path structure |
| 38 | + |
| 39 | +Given a _base_ signature storage URL configured in `registries.d` as mentioned above, |
| 40 | +and a container image stored in a docker/distribution registry using the _fully-expanded_ name |
| 41 | +_hostname_`/`_namespaces_`/`_name_{`@`_digest_,`:`_tag_} (e.g. for `docker.io/library/busybox:latest`, |
| 42 | +_namespaces_ is `library`, even if the user refers to the image using the shorter syntax as `busybox:latest`), |
| 43 | +signatures are accessed using URLs of the form |
| 44 | +> _base_`/`_namespaces_`/`_name_`@`_digest-algo_`=`_digest-value_`/signature-`_index_ |
| 45 | +
|
| 46 | +where _digest-algo_`:`_digest-value_ is a manifest digest usable for referencing the relevant image manifest |
| 47 | +(i.e. even if the user referenced the image using a tag, |
| 48 | +the signature storage is always disambiguated using digest references). |
| 49 | +Note that in the URLs used for signatures, |
| 50 | +_digest-algo_ and _digest-value_ are separated using the `=` character, |
| 51 | +not `:` like when acessing the manifest using the docker/distribution API. |
| 52 | + |
| 53 | +Within the URL, _index_ is a decimal integer (in the canonical form), starting with 1. |
| 54 | +Signatures are stored at URLs with successive _index_ values; to read all of them, start with _index_=1, |
| 55 | +and continue reading signatures and increasing _index_ as long as signatures with these _index_ values exist. |
| 56 | +Similarly, to add one more signature to an image, find the first _index_ which does not exist, and |
| 57 | +then store the new signature using that _index_ value. |
| 58 | + |
| 59 | +There is no way to list existing signatures other than iterating through the successive _index_ values, |
| 60 | +and no way to download all of the signatures at once. |
| 61 | + |
| 62 | +### Examples |
| 63 | + |
| 64 | +For a docker/distribution image available as `busybox@sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e` |
| 65 | +(or as `busybox:latest` if the `latest` tag points to to a manifest with the same digest), |
| 66 | +and with a `registries.d` configuration specifying a `sigstore` URL `https://example.com/sigstore` for the same image, |
| 67 | +the following URLs would be accessed to download all signatures: |
| 68 | +> - `https://example.com/sigstore/library/busybox@sha256=817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e/signature-1` |
| 69 | +> - `https://example.com/sigstore/library/busybox@sha256=817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e/signature-2` |
| 70 | +> - … |
| 71 | +
|
| 72 | +For a docker/distribution image available as `example.com/ns1/ns2/ns3/repo@somedigest:digestvalue` and the same |
| 73 | +`sigstore` URL, the signatures would be available at |
| 74 | +> `https://example.com/sigstore/ns1/ns2/ns3/repo@somedigest=digestvalue/signature-1` |
| 75 | +
|
| 76 | +and so on. |
| 77 | + |
| 78 | +## OpenShift-embedded registries |
| 79 | + |
| 80 | +The OpenShift-embedded registry implements the ordinary docker/distribution API, |
| 81 | +and it also exposes images through the OpenShift REST API (available through the “API master” servers). |
| 82 | + |
| 83 | +As of https://github.com/openshift/origin/pull/9181, |
| 84 | +signatures are exposed through the OpenShift API |
| 85 | +(i.e. to access the complete image, it is necessary to use both APIs, |
| 86 | +in particular to know the URLs for both the docker/distribution and the OpenShift API master endpoints). |
| 87 | + |
| 88 | +To read the signature, any user with access to an image can use the `imagestreamimages` namespaced |
| 89 | +resource to read an `Image` object and its `Signatures` array. Use only the `ImageSignature` objects |
| 90 | +which have `Type` equal to `atomic`, and read the signature from `Content`; ignore the other fields of |
| 91 | +the `ImageSignature` object. |
| 92 | + |
| 93 | +To add or remove signatures, use the cluster-wide (non-namespaced) `imagesignatures` resource, |
| 94 | +with `Type` set to `atomic` and `Content` set to the signature. Signature names must have the form |
| 95 | +_digest_`@`_per-image-name_, where _digest_ is an image manifest digest (OpenShift “image name”), |
| 96 | +and _per-image-name_ is any unique identifier. |
| 97 | + |
| 98 | +Note that because signatures are stored within the cluster-wide image objects, |
| 99 | +i.e. different namespaces can not associate different sets of signatures to the same image, |
| 100 | +updating signatures requires a cluster-wide access to the `imagesignatures` resource |
| 101 | +(by default available to the `system:image-signer` role), |
| 102 | +and deleting signatures is strongly discouraged |
| 103 | +(it deletes the signature from all namespaces which contain the same image). |
| 104 | + |
| 105 | +## (OpenShift) docker/distribution API extension |
| 106 | + |
| 107 | +As of https://github.com/openshift/origin/pull/12504/ , the OpenShift-embedded registry also provides |
| 108 | +an extension of the docker/distribution API which allows simpler access to the signatures, |
| 109 | +using only the docker/distribution API endpoint. |
| 110 | + |
| 111 | +This API is not inherently OpenShift-specific (e.g. the client does not need to know the OpenShift API endpoint, |
| 112 | +and credentials sufficient to access the docker/distribution API server are sufficient to access signatures as well), |
| 113 | +and in the future it will be the preferred way to implement signature storage in registries. |
| 114 | + |
| 115 | +More detailed documentation of this API will be added later (after `github.com/containers/image` implements it). |
0 commit comments