-
Notifications
You must be signed in to change notification settings - Fork 272
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
Offline mode #2472
Offline mode #2472
Conversation
This commit adds an offline configuration toggle to ngclient that makes the client operate without naking network connections: * feature is useful for repositories with slow moving content where a client may run hundreds or thousands of times without the repository changing (example: sigstore root-signing) * this is strictly speaking not spec compliant This is still a work-in-progress: * Tests are currently unimplemented or broken * Current iteration allows local metadata to be expired (it's still being discussed whether this is reasonable) This is all work done by folks mentioned below (I just rebased and squashed). I did not preserve the commits as the history contains merge mistakes and reverts that would not improve our git history. The included test was not correct so was removed for now. Co-Authored-By: Emile Baez <[email protected]> Co-Authored-By: Emerson Rounds <[email protected]> Co-Authored-By: omartounsi7 <[email protected]> Signed-off-by: Jussi Kukkonen <[email protected]>
* TrustedMetadataSet does not know anything about download or remote repositories: "offline" is not a relevanet concept there. Use "respect_expiry" instead * Use the config.offline value in Updater instead of _trusted_set.offline (since the latter no longer exists) * Create a method to handle the expiry checks with respect to "respect_expiry" * Tweak an exception type Signed-off-by: Jussi Kukkonen <[email protected]>
Unrelated error cases were being handled as an attempt to "download delegated targets metadata while offline": Instead handle delegated metadata like top level metadata: separate code paths for offline and online. This way the online case ends up raising the same error in both cases. Signed-off-by: Jussi Kukkonen <[email protected]>
Signed-off-by: Jussi Kukkonen <[email protected]>
If ngclient ends in a situation where it needs to download something but is in offline mode, raise DownloadError. This res a bit of handling but is better than having different errors in different situations. Signed-off-by: Jussi Kukkonen <[email protected]>
Signed-off-by: Jussi Kukkonen <[email protected]>
Pull Request Test Coverage Report for Build 6328656993
💛 - Coveralls |
I'm still considering whether the if-else paths in Updater are in the right places:
I think the latter could be slightly more code but more understandable. Now that tests exist, I think I will try that option as well to see what it looks like. EDIT: I've now implemented the latter option in the last commit, I think it's decent |
Signed-off-by: Jussi Kukkonen <[email protected]>
Make sure the test coverage for TrustedMetadataSet remains the same even though we added the respect_expiry argument: * For the tests that are not affected, use subtests with a test matrix with two configurations: respect_expiry=True and respect_expiry=False * For the tests that are affected by respect_expiry, add a second phase in the test Signed-off-by: Jussi Kukkonen <[email protected]>
The offline cases were handled on the higher level: refresh() and _preorder_depth_first_walk(). Handle thems instead in the _load_*() methods: it ends up being slightly easier to understand. Signed-off-by: Jussi Kukkonen <[email protected]>
if self.config.offline: | ||
raise exceptions.DownloadError("Cannot download when offline") |
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 didn't introduce a new exception subclass since the context seems to make this useful enough for apps: if your updater is offline, then DownloadError means the requested action is not possible with the currently cached data.
A possible complaint on this PR is that it exposes a very specific new mode: so for example expiry is either fully respected or not at all:
I felt like exposing more knobs in the API would make it more complicated to use, to test and to build in a safe manner. I'm happy to consider doing that though if there are real use cases where these knobs would make a difference. |
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.
Great patch! Implementation-wise this looks good. I still think we should give clear answers to the following questions somewhere in the ngclient documentation:
- What exactly is the use of offline mode over not using ngclient at all when offline?
- More specifically, what security guarantees exactly do we sacrifice in offline mode ("timeliness" alone feels to vague) and what guarantees do we preserve?
@@ -23,7 +24,13 @@ class UpdaterConfig: | |||
are used, target download URLs are formed by prefixing the filename | |||
with a hash digest of file content by default. This can be | |||
overridden by setting ``prefix_targets_with_hash`` to ``False``. | |||
|
|||
offline: When offline, Updater works normally except for two differences: First, |
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.
IMO "works normally" downplays the difference quite a bit. Wouldn't it be fairer to reverse the description and underline the difference a long the lines of ... ?
when offline, Updater does not update metadata or target files anymore. It can still be used to retrieve and vet locally available target files against locally available metadata with the important caveat that metadata expiry is ignored.
expired metadata is considered valid. Second, Updater will not | ||
download metadata or targets (if a download would be needed, a DownloadError | ||
is raised instead). This is useful for a client that has all required | ||
metadata and targets cached locally and is willing to sacrifice timeliness of |
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.
It's hard for a client to know, if they really have all the required metadata and targets cached locally, if they don't consult with the repository and ignore expirations.
Also, while I can see how it is useful for a client to use the metadata and targets cached locally, I am still sceptical about the usefulness of using the TUF client for this. In other words, what is the benefit of using the updater in offline mode vs. not using the updater at all?
We should make this clear somewhere. Probably not here in the docstring.
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.
sacrifice timeliness AND compromise recovery
I have forgotten to update this one: I discussed this with lukas and from sigstore perspective with William woodward and came these conclusions:
That should answer Lukas questions in the comment: this PR alone does not have clear security benefits. We could get protection against maliciously modified local cache (both metadata and targets) if we can assume:
So my instinct is to not merge this PR. We can re-evaluate if 1168 gets fixed. |
Fixes #2359, supersedes #2363
Summary
This is based 2363 code by Emile Baez, Emerson Rounds and omartounsi7 (see first commit). I've fixed some merge issues and linting problems, added tests and finally done some refactoring.
The PR exposes a new config option
offline: bool
. When offline, Updater behaviour changes in two ways:refresh()
,get_target_info()
andfind_cached_target()
: the operations are done using locally available metadata only.What this means is that an offline Updater can be used when client
Implementation notes
These are mostly about changes I did on top of 2363:
offline
seemed less useful. The argument is nowrespect_expiry
which I think is accurate. The expiry checks were consolidated in one method: if metadata has expired it either raises or logs a warning based onrespect_expiry
offline=True
: these try to ensure nothing is downloaded while offline, basic offline cases work and that offline errors are what we expectrespect_expiry
valuesoffline=True
does not change anything we don't expect it to change... but I feel like this is a reasonable amount of testsoffline
checks to theUpdater._load_*()
methods: You can check the commit-before-last if you want to see what the alternative option looks like