Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: fix WithCacheValidity #162

Closed
ramonpetgrave64 opened this issue May 10, 2024 · 9 comments · Fixed by #173
Closed

bug: fix WithCacheValidity #162

ramonpetgrave64 opened this issue May 10, 2024 · 9 comments · Fixed by #173
Assignees
Labels
bug Something isn't working

Comments

@ramonpetgrave64
Copy link

Description

I'm planning to use the tuf client's WithCacheValidity() option. When I use.WithCacheValidity(365 * 2025), it works, but not with 2024. It seems like the initial LastTimestamp is initially set to 0001-01-01 00:00:00 +0000 UTC when using the embedded tuf root.

I'm thinking it should maybe be set to the same day the embedded root was updated, so instead I could just invoke with .WithCacheValidity(7), to say that I want to use my local cache up to 7 days after after refresh. Or if it's the embedded cache, 7 days after the root was published or embedded by maintainers.

Version

sigstore-go@v0.3.0

@ramonpetgrave64 ramonpetgrave64 added the bug Something isn't working label May 10, 2024
@haydentherapper
Copy link
Contributor

cc @kommendorkapten @codysoyland can you check this out?

@kommendorkapten kommendorkapten self-assigned this May 13, 2024
@kommendorkapten
Copy link
Member

Thanks for the report, I'll take a look.

@kommendorkapten
Copy link
Member

👋 @ramonpetgrave64

I can't reproduce your behaviour, it works with both 2024 and 2025 * 365 (and side note, do you really want to cache the data for 2000+ years?).

What you may be experiencing is that regardless what you set for cache validity the first run, it will never work (a TUF update will always be performed). But the run after that can be successful as there is a cache.
The initial cache value of the last time stamp is not relevant, as after a successful TUF update the current time is persisted.

The reason we must perform a full TUF update the first time is because the embedded TUF root is not complete, it's just the root. There are a handful more files that must be downloaded. Embedding a complete TUF repository that would be valid to use on the first invocation would not be feasible, as the TUF timestamp file have an expiration of only seven days, and in practice the embedded TUF repository would always be in an expired state so a full update would be required.

@ramonpetgrave64
Copy link
Author

@kommendorkapten I forgot to mention that my intention is that there should be no TUF upon first run. And no I wouldn't want it for 2000+ years, only ~2024 years from the year 0 CE. Also I tested it by setting my firewall to block connections sudo ufw deny https.

I had assumed that the entirety of my local TUF cache at ~/.sigstore is what's already embedded at compile-time.

Still, It would be nicer for WithCacheValidity(7) to have the ability bypass the initial update so long as my local cache is recent enough.

@haydentherapper
Copy link
Contributor

I chatted with @ramonpetgrave64 offline about this issue. The request is to initialize a TUF client from locally cached metadata without making any network requests, assuming the local metadata is still valid per the timestamp.

This is currently supported by setting UnsafeLocalMode in the underlying go-tuf updater. This is actually what we are already doing when we initialize a new Sigstore TUF client. On first init, we set UnsafeLocalMode so when Refresh, it will not do an online refresh.

@ramonpetgrave64, how were you initializing the Sigstore TUF client? If you're calling client.New() with CacheValidity > 0 and a valid up-to-date cache at CachePath, then no network calls should be made.

@ramonpetgrave64
Copy link
Author

ramonpetgrave64 commented May 14, 2024

@haydentherapper Yes, the problem, in other words, is that the client initially thinks that LastTimestamp is year 0 CE, requiring me to make the client in this way:

import sigstoreTuf "github.com/sigstore/sigstore-go/pkg/tuf"

...

daysSinceCommonEra := 739385 // if today were 14 May 2024 CE
opts := sigstoreTuf.DefaultOptions().WithCacheValidity(daysSinceCommonEra + 7)
client, err := sigstoreTuf.New(opts)

Instead, LastTimestamp should maybe be the the date of the embedded root or the date of my local cache.

@haydentherapper
Copy link
Contributor

haydentherapper commented May 14, 2024

Ah, yes, this is an issue for the first time the client is initialized, before the configuration file has been created. I'm looking at how to fix this now.

@haydentherapper
Copy link
Contributor

In the short-term, using ForceCache should be sufficient, as this forces the cache to be used always up until the metadata has expired.

@kommendorkapten
Copy link
Member

You can fix this by creating the config file manually that sets the timestamp when the cache was last updated. If you populate the TUF cache, just write the config file too:

~/.sigstore/root $ ls -l
total 8
drwxr-xr-x  7 kommendorkapten  staff  224 May 14 09:08 tuf-repo-cdn.sigstore.dev/
-rw-------  1 kommendorkapten  staff   52 May 15 07:54 tuf-repo-cdn.sigstore.dev.json
~/.sigstore/root $ cat tuf-repo-cdn.sigstore.dev.json| jq
{
  "lastTimestamp": "2024-05-15T07:54:47.369154+02:00"
}

I would not think any functionality of trying to understand a cache written by a third party actor should be the responsibility of sigstore-go. If you manually prepare this data, and try to mimic the behaviour I think that's fine, but something you should be in control over.

The reason we write this timestamp file is to be able to know when the last TUF update was performed, to understand if it's time perform a new TUF update.

And also, I just want to point out that caching TUF metadata is not compliant with the TUF spec, you may miss out on important updates if you cache for a long time, and when the TUF timestamp is expired, a TUF update will happen again (this is usually a short period of time, like seven days).

If you want to force a situation where no network calls are made, it's better to provide the trusted_root.json manually, without going the extra miles of fetching it via a TUF repo, as this is the only way to truly operate in an air-gapped environment. See this example of how that can be done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants