owned this note
owned this note
Published
Linked with GitHub
---
tags: notary,tuf
---
# Storing TUF metadata under an image
The idea here is to show some possibilities of storing TUF metadata and other files and associate with an image.
:::danger
I don't know if **roots.json** should be side loaded or distributed or discoverd through side repositories.
:::
```bash=
export REPO=localhost:5000/alpine
export IMAGE=localhost:5000/alpine:3.9
docker pull alpine:3.9
docker tag alpine:3.9 localhost:5000/alpine:3.9
docker push $IMAGE
```
```bash
export DIGEST=$(oras discover -o json $IMAGE | jq -r .digest)
```
## Option 1 - Bundle up everything
```bash
export IMAGE=localhost:5000/alpine:3.9
export REPO=localhost:5000/alpine
export DIGEST=$(oras discover -o json $IMAGE | jq -r .digest)
oras push $REPO \
--artifact-type tuf/bundle \
--subject $REPO:$DIGEST \
./roots.json ./metadata.json ./targets.json
```
You can see that all the files are uploaded under one manifest as different blobs
```json
curl -L -s http://localhost:5000/v2/alpine/manifests/sha256:8084e10821c43ee22b3d416cc6565ba6c2abe11d3fda0bb6d638b214dc8fdf1c | jq
{
"artifactType": "tuf/bundle",
"blobs": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0,
"annotations": {
"org.opencontainers.image.title": "roots.json"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0,
"annotations": {
"org.opencontainers.image.title": "metadata.json"
}
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0,
"annotations": {
"org.opencontainers.image.title": "targets.json"
}
}
],
"subject": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:65b3a80ebe7471beecbc090c5b2cdd0aafeaefa0715f8f12e40dc918a3a70e32",
"size": 528
}
}
```
## Option 2 -- Bundle as different entries
Push individual files if you don't want to bundle it all together
```bash
oras push $REPO \
--artifact-type tuf/root \
--subject $REPO:$DIGEST \
./roots.json
```
```bash
oras push $REPO \
--artifact-type tuf/metadata \
--subject $REPO:$DIGEST \
./metadata.json
```
```bash
oras push $REPO \
--artifact-type tuf/target \
--subject $REPO:$DIGEST \
./targets.json
```
```
oras discover -o tree $IMAGE
localhost:5000/alpine:3.9
├── tuf/root
│ └── sha256:5e7750fa93af8b5f9afe4a8e1cdd2e7928df6c5d402deff02982fa13f4003138
├── tuf/target
│ └── sha256:fc272008dc868aa6fdcfab1adbbd5482d5f42809f26dc3294fad2e4a408da17e
└── tuf/metadata
```
## Option 3 - Push json with a tag into another repository or same repository
```bash=
export TUF_REPO=localhost:5000/tuf-repository
oras push localhost:5000/tuf-repository:root\
--artifact-type tuf/root \
./roots.json
```
## Option 4 - Bundle up everything and push to a fixed tag
Having artifact reference manifest per image may cause inefficient trust collection update in `O(n)` where `n` is the number of signed images. Assuming you have `n` unique tagged manifests in a repository with TUF metadata associated, you need to update all `n` artifact reference manifests if you want to push the `(n+1)`th manifest.
Therefore, it makes more sense to store the full bundle at a fixed place. Since `timestamp` role is special and it is usually updated quickly, we can store it in another tag.
To push a trusted image, the client gets the latest trust collection, signs the image with role keys, and update the trust collection. After that, the client pushes the updated trust metadata to the registry as follows.
```bash
REPO=localhost:5000/alpine
# Push trust metadata to the remote registry
oras push $REPO:trust \
--artifact-type tuf/bundle \
roots.json snapshot.json targets.json targets/releases.json
# Obtain the manfiest digest by parsing the output of the previous oras command
DIGEST=$(get_manifest_digest)
# Periodically signs and push timestamp signature to the remote registry.
# The timestamp sigature is associated with the TUF bundle.
oras push $REPO:trust-timestamp \
--artifact-type tuf/timestamp \
--subject $REPO:$DIGEST \
timestamp.json
```
To pull a trusted image, the client pulls `$REPO:trust-timestamp` for the timestamp. After verifying `timestamp.json`, the client can directly fetch the related metadata files and verify them accordingly.
Alternatively, the client can discover the referrer manifest for timestamp by
```bash
oras discover $REPO:trust
```
If the client accepts degraded security without timestamp, it is OK to just verify images against the metadata stored in `$REPO:trust`.
The above idea is a MVP for the current Docker Content Trust / Docker Notary model. If the trust collection is not repository scoped but registry scoped, we can store the bundle at the fixed repository with fixed tags.