tlogistry.dev

tlogistry.dev is a Docker container image registry implementation that redirects requests to other public image registries. When it receives a request for an image manifest by mutable tag, it collects the immutable digest associated with that tag, and records it in Sigstore’s transparency log, Rekor.

On subsequent requests for a manifest by tag, it checks Rekor to see if it’s seen that tag before, and fails if the previously recorded digest doesn’t match the current one.

The effect is transparently verifiable immutable tags for public images, for any public image registry, without trusting that the registry actually blocks tag updates.

How To Use It

Instead of:

docker pull alpine:3.16.0

Just add tlogistry.dev/:

docker pull tlogistry.dev/alpine:3.16.0

Or, in your Dockerfile, instead of this:

FROM alpine:3.16.0
...

Just add tlogistry.dev/:

FROM tlogistry.dev/alpine:3.16.0
...

How It Works

When you pull an image through tlogistry.dev, requests for manifests and blobs by immutable content-addressed digest are simply forwared – tlogistry.dev doesn’t store any data, it just forwards your request to the real registry.

When you pull an image manifest by tag, tlogistry.dev proxies the request from the real registry if it can. Before it serves the manifest back to you, it notes the manifest’s digest as reported by the real registry.

It then queries Rekor to see if there have been any previously reported sightings of your image by tag. If so, and if the previous records point to the same digest it’s about to serve, it serves the request. If the digest doesn’t match, that means someone updated the tag, and the proxied request fails. If there wasn’t a previous record of this image by tag, it writes one in Rekor for next time.

The service runs on Google Cloud Run, and entries in Rekor contain a keyless signature (using Sigstore’s code signing cerificate authority, Fulcio) associated with the service’s service account. The instance’s service account is tlogistry@kontaindotme.iam.gserviceaccount.com.

When a manifest request consults Rekor, information about the associated entry is included in headers in the response:

--> GET https://tlogistry.dev/v2/registry.example.biz/my/image/manifests/v1.2.3

HTTP/2.0 200 OK
...
Tlog-Integratedtime: 2022-06-28T13:03:37Z
Tlog-Logindex: 2787015
Tlog-Uuid: 362f8ecba72f432641632fca55dd510f1efcf89105458562f5d5e828262762b5e1ef276ec6d7a00b
...

If the request resulted in a new entry being created in Rekor (i.e., if this was the first time the registry has seen the tag), the Tlog-First-Seen: true header is also set in the response.

Deploying

gcloud auth login
gcloud auth application-default login
terraform init
terraform apply -var project=[MY-PROJECT]

This will build the app with ko and deploy it to your project.

By default it deploys in us-east4, but you can change this with -var region=[MY-REGION].

The generated Cloud Run URL will be something like https://tlogistry-blahblah-uk.a.run.app, which you can interact with using:

docker pull tlogistry-blahblah-uk.a.run.app/alpine:3.16.0

Frequently Asked Questions

What about :latest?

The :latest tag is conventionally updated to point to whatever the “latest” version of an artifact is.

tlogistry.dev doesn’t treat :latest differently from any other tag – the first time it’s asked to fetch alpine:latest, it will record what digest that tag points to, and prevent future requests that would serve different content.

This means that the first time you request any image by :latest using tlogistry.dev, that version will be frozen in time. If :latest is updated to point to something else, it will not be able to be pulled through tlogistry.dev, as with all tags.

It is a convenience, but it’s also an antipattern if you want reliable, consistent behavior from your container images.

Aren’t I just trusting tlogistry.dev not to mutate my tags / sell my data / mine bitcoin?

Oh you are clever. Yes you are.

If you don’t want to trust me, you can run an instance of this service yourself. Each unique instance of the service runs with a unique GCP service account, and only records written by that service account are accepted when considering entries in Rekor.

If you don’t want to trust immutable tags at all, I recommend pulling images by content-addressed immutable digests.