Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Refactor delegations to hopefuly be more secure #1089

Merged
merged 4 commits into from
Feb 15, 2019
Merged

Conversation

OYTIS
Copy link
Contributor

@OYTIS OYTIS commented Feb 8, 2019

Also, this enables nested delegations

Signed-off-by: Anton Gerasimov [email protected]

@OYTIS OYTIS added the not-ready label Feb 8, 2019
@OYTIS OYTIS force-pushed the feat/secure-delegations branch from 252be62 to 5bde49f Compare February 11, 2019 09:47
@codecov-io
Copy link

codecov-io commented Feb 11, 2019

Codecov Report

Merging #1089 into master will decrease coverage by 0.02%.
The diff coverage is 84.16%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1089      +/-   ##
==========================================
- Coverage    75.6%   75.58%   -0.03%     
==========================================
  Files         158      158              
  Lines        9346     9431      +85     
==========================================
+ Hits         7066     7128      +62     
- Misses       2280     2303      +23
Impacted Files Coverage Δ
src/libaktualizr/storage/invstorage.h 97.43% <ø> (ø) ⬆️
src/libaktualizr/storage/sqlstorage.h 50% <ø> (ø) ⬆️
src/libaktualizr/primary/sotauptaneclient.h 100% <ø> (ø) ⬆️
src/aktualizr_repo/image_repo.h 100% <ø> (ø) ⬆️
src/libaktualizr/uptane/tuf.h 92.79% <ø> (-0.19%) ⬇️
src/aktualizr_repo/uptane_repo.h 100% <ø> (ø) ⬆️
src/libaktualizr/storage/sqlstorage.cc 72.42% <0%> (-0.85%) ⬇️
src/aktualizr_repo/main.cc 78.68% <100%> (+0.91%) ⬆️
src/libaktualizr/uptane/imagesrepository.h 100% <100%> (ø) ⬆️
src/aktualizr_repo/uptane_repo.cc 96.55% <100%> (ø) ⬆️
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e9c7a25...155b39b. Read the comment docs.

@OYTIS OYTIS force-pushed the feat/secure-delegations branch from 5bde49f to 10fa87a Compare February 11, 2019 13:13
@OYTIS OYTIS removed the not-ready label Feb 11, 2019
Copy link
Collaborator

@pattivacek pattivacek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good. Nesting logic looks good except for the version issue, and I like not downloading delegations unless/until we need them.

I don't think we have support anywhere for actually generating nested delegations, though. I don't think it'd be too hard to add to aktualizr-repo, and we need some way to test that this logic works. What do you think?

static std::shared_ptr<Uptane::Targets> verifyDelegation(const std::string& delegation_raw, const Uptane::Role& role,
const Targets& parent_target);
std::shared_ptr<Uptane::Targets> getTrustedTargets() { return targets; }

private:
// Map from role name -> metadata ("targets" for top-level):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is no longer relevant.

@@ -29,9 +29,13 @@ class ImagesRepository : public RepositoryCommon {

Exception getLastException() const { return last_exception; }

static std::shared_ptr<Uptane::Targets> verifyDelegation(const std::string& delegation_raw, const Uptane::Role& role,
const Targets& parent_target);
std::shared_ptr<Uptane::Targets> getTrustedTargets() { return targets; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Trusted targets" is an interesting name. Based on the line auto toplevel_targets = images_repo.getTrustedTargets(); it appears to be just the toplevel targets; is that accurate? Maybe a comment in the header to explain what is meant by "trusted" here would be helpful.

delegation = Uptane::ImagesRepository::verifyDelegation(delegation_meta, delegate_role, cur_targets);
if (delegation == nullptr) {
storage->deleteDelegation(delegate_role);
delegation_meta.clear();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure I have this straight: we only delete a delegation if verification fails? So if a delegation is removed and later re-added, it wouldn't get deleted after removal, but hopefully verification will fail and then it will get deleted. Right?

if (!uptane_fetcher->fetchLatestRole(&delegation_meta, Uptane::kMaxImagesTargetsSize,
Uptane::RepositoryType::Image(), delegate_role)) {
throw std::runtime_error(std::string("Couldn't fetch ") +
delegate_role.ToString()); // TODO: connectivity exception
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we still need to fetch even if we have a stored version in case the version has incremented? Maybe I'm confusing something with versions, but this seems like it won't ever fetch new versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, you're right. So storing delegations in a persistent storage doesn't make any sense at all, we don't know what's there until we go to the server. That's annoying.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can propose one of the two procedures then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either:

  1. At the beginning of update cycle clear all delegations.
  2. Cache delegations during the update cycle.
  3. Use cached version when it's available, download otherwise.

This way we can avoid downloading the same delegation over for different targets in the same update cycle, but still can't share them between the update cycles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or:

  1. Store delegations permanently.
  2. Use the same procedure that is implemented now on the first pass.
  3. If some a target has not been found, update delegations for it on a second pass.

This way we are more bandwith-effective, but there is a potential security problem in not being able to revoke trust to targets in delegated roles in a timely fashion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second option doesn't seem safe enough to me, but the first option is interesting. Does it make since to do any sort of comparison between old and new versions? If we have both available anyway, I wonder if there's anything intelligent we can do with them or if we really just have to ignore the old one and throw it away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what's in docker notary documentation by the way:

The snapshot key signs the snapshot metadata file, which enumerates the filenames, sizes, and hashes of the root, targets, and delegation metadata files for the collection.

Makes a lot of sense to me and resolves some of the points of confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way we can cache the delegated meta and only update it if the version in snapshot.json has been incremented. And versioning for delegated targets suddenly makes sense again.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but is that what the backend is doing? I also want to make sure TUF/Uptane are friendly to that notion, but I think they should be. To make sure I understand correctly: this seems like the second option you mentioned, but more robust, because we don't store delegations "permanently", just as long as the version specified in snapshot.json hasn't changed.

Copy link
Contributor Author

@OYTIS OYTIS Feb 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TUF spec says

The snapshot.json file is signed by the snapshot role. It lists the version numbers of all metadata on the repository, excluding timestamp.json and mirrors.json. For the root role, the hash(es), size, and version number are listed.

which doesn't contradict to what's in Notary docs, just does not name delegations explicitly. I believe that if backend doesn't add delegated roles to snapshot.json yet, it should do so. Otherwise we have weaker security guarantees for delegated targets than for top-level target, and I can't see a reason why it should be the case.

@OYTIS OYTIS force-pushed the feat/secure-delegations branch from 039657d to cfee6f9 Compare February 12, 2019 17:38
Also, this enables nested delegations

Signed-off-by: Anton Gerasimov <[email protected]>
@OYTIS OYTIS force-pushed the feat/secure-delegations branch 2 times, most recently from ffc9569 to e430f36 Compare February 13, 2019 13:19
Anton Gerasimov added 2 commits February 13, 2019 15:23
Also list delegated roles in snapshot.json

Signed-off-by: Anton Gerasimov <[email protected]>
@OYTIS OYTIS force-pushed the feat/secure-delegations branch 2 times, most recently from 8fd046f to d25c02a Compare February 14, 2019 14:50
@OYTIS OYTIS removed the not-ready label Feb 14, 2019
Copy link
Collaborator

@pattivacek pattivacek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks promising to me.

hash_exists = true;
break;
default:
break;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like my comment may have disappeared. Is it possible for the other verify functions to reuse verifyRoleHashes as well? If it's too complicated, don't worry about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verifyTargets is already using it. Other roles are not listed in snapshot.json (well, root.json is, but it makes no sense to verify it).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant specifically the hashing logic in verifySnapshot. But after looking more closely, it doesn't isn't worth trying to combine those.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patrickvacek Yes, maybe the logic of iterating over a list of hashes could be abstracted out a bit, but right now it's not too bad I think, it's just two places.

@OYTIS OYTIS force-pushed the feat/secure-delegations branch from d25c02a to 155b39b Compare February 15, 2019 11:35
Copy link
Collaborator

@pattivacek pattivacek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this actually work with the existing server-side implementation? It seems like it won't. That's okay, but worth knowing if we want to test/demo this before the server-side gets updated.

@OYTIS
Copy link
Contributor Author

OYTIS commented Feb 15, 2019

@patrickvacek Yes, that breaks compatibility with the backend as implemented now. But we have already agreed, it should be fixed there, and no-one uses it yet I believe.

@OYTIS OYTIS merged commit 64925b8 into master Feb 15, 2019
@lbonn lbonn deleted the feat/secure-delegations branch February 15, 2019 15:50
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants