-
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
TUF Offline Functionality #2363
Conversation
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'm writing my first review comments as they come to mind -- I may have some additional thoughts but would appreciate your takes on these.
- I like that it's quite clean already: I was worried it may make the code too cluttered but I think we can work with this...
- I would like for this feature to be very difficult to misuse -- that you could not e.g. accidentally download targets using an "offline updater": that seems like a user mistake. I think this can easily happen with this design
- What do you think about making "offline" something you enable at TrustedMetadataSet construction time (which practically means Updater construction time)?
- then the TrustedMetadataSet could have an
offline
attribute (orskip_expiry
attribute to be more descriptive) - it might mean some test setup changes, but the actual code may be simpler as the methods no longer need the arg and you could use the attribute in lower layers of the code (see a code comment)
- Updater could then fail loudly in
download_target()
if its being used with a TrustedMetadataSet that is offline
- then the TrustedMetadataSet could have an
- I haven't looked closely at the test yet but one additional thing we do need to test is the full use case: basically make sure we can get the locally cached target even if metadata has expired:
- (setup: make sure metadata and a target are locally cached, and that metadata is now considered expired)
- create offline Updater
- get_targetinfo() for the target
- find_cached_target()
I'm open to additional tests happening in other PRs if that seems to complicate this one too much
if not offline: | ||
self._check_final_snapshot() | ||
else: | ||
snapshot_meta = self.timestamp.signed.snapshot_meta | ||
if self.snapshot.signed.version != snapshot_meta.version: | ||
raise exceptions.BadVersionNumberError( | ||
f"Expected snapshot version {snapshot_meta.version}, " | ||
f"got {self.snapshot.signed.version}" | ||
) |
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 usually don't mind duplicating a bit of code but this has a bit of a smell... I think we could avoid this if the whole TrustedMetadataSet
instance was "offline": then the lower level code (in this instance _check_final_snapshot()
) could have the differing code paths and this code here would not need changes
even in he simpler cases, like _check_final_timestamp()
, it might make the code easier to understand if the "offline" check was made inside the function (instead of in two separate places where the function is called)...
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.
Yea we were going back and forth for a little while on how we should deal with this. We'll modify _check_final_snapshot() to have differing codepaths
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 for all the feedback! We've made changes to TrustedMetadataSet
so that it allows the offline flag to be set within this object and all methods now take advantage of this.
I'll keep an eye on this PR too but if you want to discuss things more interactively the python-tuf slack channel is a good choice (I know slack is a pain if you're not on it already, sorry) On the test results:
|
@jku we've implemented the changes you've suggested and ran a lint on our code, is there anything else we need to do? |
Have not looked at code yet but quick comments:
|
Code looks pretty good.
I've not tested this yet myself or looked at the test code, was planning to do that once lint and tests are passing -- let me know if you'd like assistance before that |
Yea, believe it's an issue with catching commits that were picked up when syncing our fork. We should be able to revert back to before the retroactive signing and just do a final signed squash |
Will take a look into this first chance I get. |
Sorry for the delay. We have been busy with graduation/post-graduation travels. We will be looking into this this weekend. |
Signed-off-by: Emile Baez <ebaezmunne@gmail.com> fixed error Signed-off-by: Emile Baez <ebaezmunne@gmail.com> nglclient updater refresh method checks for offline mode reformat of Jussi's lazy refresh closed PR. Addition of checking for offline flag passed in from client. Will try to load from local metadata if either is set. If offline is set and local metadata is expired or not available, ExpiredMetadataError will be raised. Tests added for offline being set with and without valid local metadata. (needs review). Signed-off-by: Emile Baez <ebaezmunne@gmail.com> refresh method fix. 164 tests pass, 3 warnings Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump ossf/scorecard-action from 2.1.2 to 2.1.3 Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.1.2 to 2.1.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](ossf/scorecard-action@e38b190...80e868c) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump pylint from 2.17.1 to 2.17.2 Bumps [pylint](https://github.com/PyCQA/pylint) from 2.17.1 to 2.17.2. - [Release notes](https://github.com/PyCQA/pylint/releases) - [Commits](pylint-dev/pylint@v2.17.1...v2.17.2) --- updated-dependencies: - dependency-name: pylint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump pypa/gh-action-pypi-publish from 1.8.3 to 1.8.5 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.3 to 1.8.5. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](pypa/gh-action-pypi-publish@48b317d...0bf742b) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump mypy from 1.1.1 to 1.2.0 Bumps [mypy](https://github.com/python/mypy) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/python/mypy/releases) - [Commits](python/mypy@v1.1.1...v1.2.0) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump coverage from 7.2.2 to 7.2.3 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.2 to 7.2.3. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](nedbat/coveragepy@7.2.2...7.2.3) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump github/codeql-action from 2.2.9 to 2.2.11 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.9 to 2.2.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@04df126...d186a2a) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump actions/github-script from 6.4.0 to 6.4.1 Bumps [actions/github-script](https://github.com/actions/github-script) from 6.4.0 to 6.4.1. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](actions/github-script@98814c5...d7906e4) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> Removed lazy refresh config Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> Removed lazy refresh configuration option Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> Removed lazy refresh tests and updated offline tests Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump cryptography from 40.0.1 to 40.0.2 Bumps [cryptography](https://github.com/pyca/cryptography) from 40.0.1 to 40.0.2. - [Release notes](https://github.com/pyca/cryptography/releases) - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](pyca/cryptography@40.0.1...40.0.2) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump actions/checkout from 3.5.0 to 3.5.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@8f4b7f8...8e5e7e5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump github/codeql-action from 2.2.11 to 2.2.12 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.11 to 2.2.12. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@d186a2a...7df0ce3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> disabled expiry checks in offline mode. updated testing Signed-off-by: Emile Baez <ebaezmunne@gmail.com> NetworkUnavailableError exception added More descriptive exception added for offline mode error case. Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump securesystemslib[crypto,pynacl] from 0.27.0 to 0.28.0 Bumps [securesystemslib[crypto,pynacl]](https://github.com/secure-systems-lab/securesystemslib) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/secure-systems-lab/securesystemslib/releases) - [Changelog](https://github.com/secure-systems-lab/securesystemslib/blob/main/CHANGELOG.md) - [Commits](secure-systems-lab/securesystemslib@v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: securesystemslib[crypto,pynacl] dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump actions/setup-python from 4.5.0 to 4.6.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@d27e3f3...57ded4d) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump github/codeql-action from 2.2.12 to 2.3.0 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.12 to 2.3.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@7df0ce3...b2c19fb) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump pylint from 2.17.2 to 2.17.3 Bumps [pylint](https://github.com/PyCQA/pylint) from 2.17.2 to 2.17.3. - [Release notes](https://github.com/PyCQA/pylint/releases) - [Commits](pylint-dev/pylint@v2.17.2...v2.17.3) --- updated-dependencies: - dependency-name: pylint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> Removed vestigial code from updater Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> Removed unused error Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump coverage from 7.2.3 to 7.2.4 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.3 to 7.2.4. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](nedbat/coveragepy@7.2.3...7.2.4) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump github/codeql-action from 2.3.0 to 2.3.2 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.3.0 to 2.3.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@b2c19fb...f3feb00) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump requests from 2.28.2 to 2.29.0 Bumps [requests](https://github.com/psf/requests) from 2.28.2 to 2.29.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](psf/requests@v2.28.2...v2.29.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> build(deps): bump coverage from 7.2.4 to 7.2.5 Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.4 to 7.2.5. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](nedbat/coveragepy@7.2.4...7.2.5) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com> offline mode added to TrustedMetadataSet creation of TrustedMetadataSet object can take additional argument that describes whether client wants to be in offline mode. Code linted according to tox -e lint. Signed-off-by: Emile Baez <ebaezmunne@gmail.com>
…downloading data in offline mode Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com>
Signed-off-by: omartounsi7 <62721212+omartounsi7@users.noreply.github.com> Signed-off-by: Emile Baez <ebaezmunne@gmail.com>
We've made the changes you recommended; are there any other fixes you want us to make before this is in a state to merge? We do think our test cases are a little rudimentary at the moment and could use some outside input. @jku |
…oo-many-instance-attributes Signed-off-by: Emile Baez <ebaezmunne@gmail.com> typo fix Signed-off-by: Emile Baez <ebaezmunne@gmail.com>
Yes the test case(s) do need a bit of work...
but lets's get the PR to an otherwise good state first: there is still something wrong in the first commit. It now contains changes that are clearly unrelated -- I'm guessing these are merged commits from upstream develop to your branch that were all squashed into one commit with your changes? I did say that "Squashing your changes to single commit is ok" but the effective term there was your changes :) Please try to fix that. If it feels like you can't salvage it, let me know and I'll give it a shot. Rough outline of what I'd try:
For next PR I would suggest using a topic branch (like in this case a branch called "offline" instead of "develop"): keeping rebases and merges in check is easier when branches don't all have the same name |
I should be able to directly fix the conflicts via text editor here. I'm pretty sure just discarding all the code on this fork that's conflicting with the main repo should fix the issue |
Signed-off-by: Emerson Rounds <65183924+emboman13@users.noreply.github.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.
The following suggestions about using the _trusted_set offline state instead of config.offline are related to the confusion that I think is going on in the test case.
The suggestions should not affect functionality if caller is doing the right thing, but if the user incorrectly modifies the config during the lifetime of the updater (like I believe current test does), our understanding of "offline" should not change: The TrustedMetadataSet offline state (and thus Updater offline state) should be immutable.
I'll have a closer look at testing next next week but the comments so far might already be useful
tuf/ngclient/updater.py
Outdated
@@ -129,6 +131,17 @@ def refresh(self) -> None: | |||
DownloadError: Download of a metadata file failed in some way | |||
""" | |||
|
|||
if self.config.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.
This should probably check self._trusted_set.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.
Would that be necessary? That is just set as self.config,offline on line 106-107?
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.
self.config.offline
can be modified by the user after the Updater has been initialized but before the other methods are called. This does not make a lot of sense, but technically it is possible. In other words, if we check check self.config.offline here it may well be False even though self._trusted_set has been initialized as offline.
The current test case seems to be a good example of this.
tuf/ngclient/updater.py
Outdated
|
||
Returns: | ||
Local path to downloaded file | ||
""" | ||
|
||
if self.config.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.
This should probably check self._trusted_set.offline
tuf/ngclient/updater.py
Outdated
@@ -386,6 +403,9 @@ def _load_targets(self, role: str, parent_role: str) -> Metadata[Targets]: | |||
logger.debug("Local %s is valid: not downloading new one", role) | |||
return delegated_targets | |||
except (OSError, exceptions.RepositoryError) as e: | |||
# fails if local data is unavalible and in offline mode | |||
if self.config.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.
This should probably check self._trusted_set.offline
Thanks for all the feedback! We've drafted a correction adding the optional flag for We realized the first case we had written might not be needed at all. Here, we wanted to make sure the Updater (without metadata) behaved as it did before, with the addition of having the offline flag (set to False). Before committing anything, do you think we should remove this case altogether, given that it should technically already be tested for?
Regarding the test for
|
I'm not quite sure how to answer since the test as is does not quite work... I'll write some thoughts down hope it helps.
|
Gentle ping on this: sigstore/sigstore-python#483 is blocked upstream by an offline configuration for TUF; it'd be great to have an expected/supported API here rather than brute-forcing offline support in on our end 🙂 |
Thanks. I'll be out for next week but I can put a bit of effort in this after that. Can you confirm that #2359 (comment) describes the functionality you want? There's clearly multiple possible definitions of "offline" and I don't want python-tuf to end up with one that isn't useful: adding complexity to the verification code is not great so I want to be sure it's at least useful complexity |
Yep, I just took a look and left a follow-up there -- I think it's 99% of what we want, with one open question about whether we should "fail open" in offline mode with expired metadata. |
Signed-off-by: Emile Baez <ebaezmunne@gmail.com>
I've dropped the ball on this one (I think I was waiting for some tests to appear), sorry. I will have a look this week |
This has been superseded by #2472 which contains this branch contents, loads of tests and some improvements from me. I'm sorry for not including the original commits in that branch: this because of some merge issues in this branch. I've preserved the author info with a "Co-Authored-By", I hope that is acceptable to everyone. If there's feedback on this or anything else, please comment in #2472. |
Fixes #2359
This implements an offline configuration for Python TUF. It allows for the updater to exclusively function off of local metadata, given that a configuration in its constructor is set.
Changes are located within ngclient; specifically to config.py, updater.py, and to _internal/trusted_metadata_set.py. These changes include adding an additional configuration option to config.py, changing refresh() within updater.py to use said configuration option, and changing the load functions to support exclusive usage of local data.
Test implementation of this within sigstore can be found here:
https://github.com/emilejbm/sigstore-python/tree/offline-flag