-
Notifications
You must be signed in to change notification settings - Fork 124
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
Allow Initialize/NewFromEnv to reinitialize metadata if timestamp expired #893
Conversation
A change in behavior to note: Previously, if you called Initialize, then NewFromEnv, the root would only be initialized once. Now, it will be twice. Additionally, Initialize is no longer thread safe with this implementation. Lemme know if you see a better way to do this. If you want Initialize to be thread safe, I'll use a mutex. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/hold - we have had problems with thread safety with the TUF client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this
Lines 280 to 284 in 095df6c
// We may already have an up-to-date local store! Check to see if it needs to be updated. | |
trustedTimestamp, ok := trustedMeta["timestamp.json"] | |
if ok && !isExpiredTimestamp(trustedTimestamp) && !forceUpdate { | |
return t, nil | |
} |
Without the threadsafety running cosign in parallel will break, which has happened before
EDIT: ignore the threadsafety comment, but I think the timestamp still should be moved outside the singleton DoOnce since we want to check that at each call to the TUF obj
@asraa, I tried to move out all of the code before and including the timestamp expiration check outside of the singleton, but the concurrency test fails. I'm not certain what is causing this, so I thought it might be quicker to add a thread-unsafe method to use in the prober, which we need to unblock soon. @vaikas, would you be able to take a look at this and let me know where there were thread safety issues in policy-controller? |
The problems we were seeing surfaced there, but were fixed here (@asraa and I were collaborating on the fix, so there's two PRs but only one got merged): There was also some other investigation here: Looks like the test for multiple access (TestConcurrentAccess) is there still in client_test. There were also bunch of cleanup issues here: I jotted it down in the ☝️ but just stating it here as well, I'm curious about the need for this being implemented as a singleton instead of using RW locks while operating on the underlying data structures. |
Thanks, I’ll take a look tomorrow! Any opposition to what’s here? We are gonna have weekly alerts til we get a fix in, so I’d prefer to just get a quick fix in and refactor later. |
pkg/tuf/client.go
Outdated
} | ||
|
||
// Initialize creates a new TUF client, if one has not already been initialized, | ||
// and writes the cache to disk if requested. Thread safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't seem threadsafe from a quick glance and from the PR description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also probably worth while to note in the comments that this is the singleton instance. Or make it somehow clear that it does not necessairly return a new TUF client, but could return a previously created one. Reasoning being that it would then be easier to understand why this is different from InitializeAlways.
pkg/tuf/client.go
Outdated
return nil | ||
} | ||
|
||
// InitializeAlways creates a new TUF client. Repeated calls to Initialize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, Repeated calls to InitializeAlways?
This now allows one to have multiple clients simultaneously which I was having a bit of a rub before. I think by not forcing a singleton is nice, but the one thing that might be problematic still is the use of rootCacheDir
below since now different clients will have a shared state in the form of the local cache, which is problematic. I think if you allow the caller to specify the cachedir here would be better.
And while you're at it, I think rather than having the getEmbedded live inside the function, taking in the fs.FS as an argument would be better, and would probably make the testing easier so one doesn't have to override the function (which IIRC caused some hilarity in the past as well. If not here, surely elsewhere :) ).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you allow the caller to specify the cachedir here would be better.
Agree, but in practice if you used different clients then you'd need to access their roots / objs separately, but those are all hidden inside methods like
sigstore/pkg/fulcioroots/fulcioroots.go
Line 101 in 04379fd
func initRoots() ([]*x509.Certificate, []*x509.Certificate, error) { |
NewFromEnv
.
(Lessons learned for a better second implementation..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
schucks :( I figure those should be hoisted to sigstore TUF client (something like GetFulcioRoots) since that's what (I think at least) the sigstore TUF client is adding on top of raw TUF, semantic meaning to various keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
figure those should be hoisted to sigstore TUF client (something like GetFulcioRoots) since that's what (I think at least) the sigstore TUF client is adding on top of raw TUF, semantic meaning to various keys.
YES! I would have liked an API like:
GetTufObj(TufParams) -> TufObj
TufObj.GetFulcio -> Fulcio
```
Idea maybe to explore: lock the singleton resets with a read/write mutex (that way it's safe to reset the singleton and block any readers from accessing the tufObj) |
This changes the singleton to only initialize the client once, but on every invocation, check if the timestamp is expired. I wrapped all of this in a mutex to prevent concurrent updates (I'm not sure if this would be safe, I assume the TUF object is not thread-safe). Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
68a20be
to
50c8aa4
Compare
@asraa @vaikas Reworked this, ptal What I kept:
Changes:
|
Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! LGTM, sorry for the late review.
This changes Initialize/NewFromEnv to always check if the timestamp is expired, while still locking access to the TUF object.
Ref: https://github.com/sigstore/public-good-instance/issues/1161
Signed-off-by: Hayden Blauzvern hblauzvern@google.com
Summary
Release Note
Documentation