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

Support deprecating old root keys after rotation #942

Merged
merged 7 commits into from
Jan 10, 2017

Conversation

ecordell
Copy link
Contributor

@ecordell ecordell commented Sep 7, 2016

This adds support for root key rotation as described in #835.

Previously, old root keys needed to sign for all subsequent root.json files in perpetuity. This changes root rotation so that new keys are trusted if signed by the previous root key/threshold. To support this behavior, root files can now be requested by version number from the server.

This also adds an additional command line flag for rotate, --key, which allows specifying keys to rotate the root to. This supports specifying multiple keys, but all must be available to sign locally (i.e. this doesn't support threshold/witness behavior at the moment).

Example:

# test/collection root.json signed by A, B
$ notary key rotate test/collection root --key=C --key=D
# test/collection root.json now signed by A, B, C, D
$ notary key rotate test/collection root --key=E
# test/collection root.json now signed by C, D, E

Closes #1014
Closes #835

@docker-jenkins
Copy link

Can one of the admins verify this patch?

@@ -13,11 +13,12 @@ import (
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/utils"

"os"
Copy link
Contributor

Choose a reason for hiding this comment

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

would you mind if moving this package up to those canonical packages

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do - could've sworn gofmt does this for you.

@@ -906,7 +909,7 @@ func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*TUFClient, e
// RotateKey removes all existing keys associated with the role, and either
// creates and adds one new key or delegates managing the key to the server.
// These changes are staged in a changelist until publish is called.
func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error {
func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool, keyList []string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we document what keyList is in the function description?

@riyazdf
Copy link
Contributor

riyazdf commented Sep 9, 2016

thanks @ecordell for your work on this! I have a couple of things:

  • I want to echo my comment about whether we should allow non-root roles to rotate to multiple keys -- it's fine with me but if so we should make sure it's adequately tested in unit and integration tests. I'm also fine with leaving the scope of this PR to only root-roles and expanding on this later.
  • nit: can we rename the PR to something like Support specifying specific keys on root rotation? It's a little confusing because root key rotation is technically already supported :)

@ecordell
Copy link
Contributor Author

@riyazdf thanks for taking a look!

  1. Responded inline, but currently this will fail for non-root roles because the non-root keys are not generally encrypted. I dislike that this is an implicit restriction so I think we should choose one way or the other and I'll update.
  2. I'll update the title

@ecordell ecordell changed the title Support root key rotation Support deprecating old root keys after rotation Sep 13, 2016
@ecordell ecordell force-pushed the root-key-rotation branch 9 times, most recently from 4bc591c to 5ec5862 Compare September 19, 2016 11:21
@@ -88,6 +92,13 @@ func (mts *MockCryptoService) GetKey(keyID string) data.PublicKey {
return nil
}

func (mts *MockCryptoService) GetKeyInfo(keyID string) (trustmanager.KeyInfo, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function can probably be removed too.

@@ -203,6 +204,11 @@ func (trust *NotarySigner) GetKey(keyid string) data.PublicKey {
return pubKey
}

// GetKeyInfo returns the gun and role given a keyID
Copy link
Contributor

Choose a reason for hiding this comment

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

This function can probably be removed too.

@@ -215,6 +215,16 @@ func (rdb *RethinkDBKeyStore) GetKey(keyID string) data.PublicKey {
return data.NewPublicKey(dbPrivateKey.Algorithm, dbPrivateKey.Public)
}

// GetKeyInfo returns the gun and role given a KeyID, and does not activate the key
Copy link
Contributor

Choose a reason for hiding this comment

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

This function can probably be removed too.

@@ -228,6 +228,15 @@ func (s *SQLKeyDBStore) GetKey(keyID string) data.PublicKey {
return data.NewPublicKey(privKey.Algorithm, []byte(privKey.Public))
}

// GetKeyInfo returns role and GUN info of a key by ID
Copy link
Contributor

Choose a reason for hiding this comment

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

This function can probably be removed too.

@cyli
Copy link
Contributor

cyli commented Jan 6, 2017

@ecordell
Thanks for adding the version skip test! (2) TestRootRoleInvariant tests that the latest root does not validate, but not intermediate downloaded roots, and (3) TestUpdateFailsIfServerRootKeyChangedWithoutMultiSign tests that if the cached root is expired, then an update succeeds so long as the new root is valid.

We were thinking for (3) something like having the local cache at v1 (either expired or not), and downloading an expired v2 and a valid v3? It should successfully update to v3.

Similarly for (2), except v2 is not properly rotated. The only reason being because some checks are skipped for intermediate roots (like checksum and expiry), and this would validate that the root signing is validated for the intermediate roots.

@ecordell
Copy link
Contributor Author

ecordell commented Jan 7, 2017

@cyli I gotcha - I added those extra tests to be safe, thanks for clarifying.

I also removed those other GetKeyInfo methods - somehow I missed them, sorry about that.

@cyli
Copy link
Contributor

cyli commented Jan 7, 2017

@ecordell Thanks for adding them!

I also removed those other GetKeyInfo methods - somehow I missed them, sorry about that.

Not at all, easy to lose track :)

@cyli
Copy link
Contributor

cyli commented Jan 7, 2017

jenkins, test this please

@cyli
Copy link
Contributor

cyli commented Jan 7, 2017

LGTM! Thank you so much for all your work and patience on this @ecordell!

@ecordell
Copy link
Contributor Author

ecordell commented Jan 9, 2017

Thanks! I've rebased on master, so hopefully everything will be good shortly.

@cyli
Copy link
Contributor

cyli commented Jan 9, 2017

Jenkins, test this please


return oldKeys
}

func oldRootVersionName(version int) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Followup PR: I think we can remove this function. I don't think it's used anywhere with the new root rotation logic.

Copy link
Contributor

@endophage endophage left a comment

Choose a reason for hiding this comment

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

LGTM.

@ecordell thank you for all the hard work and perseverance on this. Conceptually and concretely I think this root rotation methodology is an outstanding improvement to both the TUF spec and Notary.

Copy link
Contributor

@riyazdf riyazdf left a comment

Choose a reason for hiding this comment

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

couple of nits between myself and @endophage's comment that can be followed up in a subsequent PR.

Overall LGTM, thank you for continuously working through this PR! 🎉

@@ -700,11 +714,84 @@ func signRootIfNecessary(updates map[string][]byte, repo *tuf.Repo, initialPubli
return nil
}

// Fetch back a `legacyVersions` number of roots files, collect the root public keys
// This includes old `root` roles as well as legacy versioned root roles, e.g. `1.root`
func (r *NotaryRepository) oldKeysForLegacyClientSupport(legacyVersions int, initialPublish bool) (data.KeyList, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

followup PR: I'm pretty happy with this function but would like to add some unit tests around overly large or negative legacyVersions and the skipping logic

@HuKeping
Copy link
Contributor

Thanks for your patience and great work on this @ecordell !

@ecordell
Copy link
Contributor Author

Thanks for all of the reviews! I can't merge myself if someone with write access would like to :) Glad to finally have this wrapped up.

@endophage endophage merged commit eae6372 into notaryproject:master Jan 10, 2017
@ecordell ecordell deleted the root-key-rotation branch January 10, 2017 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants