Skip to content

Commit

Permalink
reuse a single targets key across repos
Browse files Browse the repository at this point in the history
Signed-off-by: Trishank K Kuppusamy <trishank.kuppusamy@datadoghq.com>
  • Loading branch information
trishankatdatadog committed Apr 8, 2020
1 parent 7278ac5 commit 6179f00
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 3 deletions.
61 changes: 60 additions & 1 deletion pkg/tuf/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func importRootKey(rootKey string, nRepo client.Repository, retriever notary.Pas
// Chooses the first root key available, which is initialization specific
// but should return the HW one first.
rootKeyID := rootKeyList[0]
log.Infof("SIGNY: Root key found, using: %s\n", rootKeyID)
log.Debugf("Signy found root key, using: %s\n", rootKeyID)

return []string{rootKeyID}, nil
}
Expand Down Expand Up @@ -176,6 +176,65 @@ func readKey(role data.RoleName, keyFilename string, retriever notary.PassRetrie
return privKey, nil
}

// Try to reuse a single targets key across repositories.
// FIXME: Unfortunately, short of forking Notary or sending a PR upstream, there isn't an easy way to prevent it
// from automagically creating a new, local targets key per TUF metadata repository. We fix this here by undoing
// more than one new, local targets key, and reusing any existing local targets key, just like the way Notary
// reuses the root key.
func reuseTargetsKey(r client.Repository) error {
var (
err error
thisTargetsKeyID, thatTargetsKeyID string
)

// Get all known targets keys.
targetsKeyList := r.GetCryptoService().ListKeys(data.CanonicalTargetsRole)
// Try to extract a single targets key we can reuse.
switch len(targetsKeyList) {
case 0:
err = fmt.Errorf("No targets key despite having initialized a repo!")
case 1:
log.Debug("Nothing to do, only one targets key available")
case 2:
// Get the current top-level roles.
roleWithSigs, listRolesErr := r.ListRoles()
if listRolesErr != nil {
err = listRolesErr
break
}

// Get the current targets key.
// NOTE: We do not delete it, in case the user wants to keep it.
for _, roleWithSig := range roleWithSigs {
role := roleWithSig.Role
if role.Name == data.CanonicalTargetsRole {
if len(role.KeyIDs) == 1 {
thisTargetsKeyID = role.KeyIDs[0]
log.Debugf("This targets keyid: %s", thisTargetsKeyID)
} else {
return fmt.Errorf("This targets role has more than 1 key!")
}
}
}

// Get and reuse the other targets key.
for _, keyID := range targetsKeyList {
if keyID != thisTargetsKeyID {
thatTargetsKeyID = keyID
log.Debugf("That targets keyID: %s", thatTargetsKeyID)
break
}
log.Debugf("Before rotating targets key from %s to %s", thisTargetsKeyID, thatTargetsKeyID)
err = r.RotateKey(data.CanonicalTargetsRole, false, []string{thatTargetsKeyID})
log.Debugf("After targets key rotation")
}
default:
err = fmt.Errorf("There are more than 2 targets keys!")
}

return err
}

func getPassphraseRetriever() notary.PassRetriever {
baseRetriever := passphrase.PromptRetriever()
env := map[string]string{
Expand Down
10 changes: 9 additions & 1 deletion pkg/tuf/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,24 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou
if _, err = repo.ListTargets(); err != nil {
switch err.(type) {
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
// Reuse root key.
rootKeyIDs, err := importRootKey(rootKey, repo, getPassphraseRetriever())
if err != nil {
return nil, err
}

// 2nd variadic argument is to indicate that snapshot is managed remotely.
// NOTE: 2nd variadic argument is to indicate that snapshot is managed remotely.
// The impact of a timestamp + snapshot key compromise is not terrible:
// https://docs.docker.com/notary/service_architecture/#threat-model
if err = repo.Initialize(rootKeyIDs, data.CanonicalSnapshotRole); err != nil {
return nil, fmt.Errorf("cannot initialize repo: %v", err)
}

// Reuse targets key.
if err = reuseTargetsKey(repo); err != nil {
return nil, fmt.Errorf("cannot reuse targets keys %v", err)
}

default:
return nil, fmt.Errorf("cannot list targets: %v", err)
}
Expand Down
7 changes: 6 additions & 1 deletion scripts/live-reload.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#!/bin/bash

echo "Installing fswatch..."
brew install fswatch
echo

echo "Building..."
make install
echo

# https://emcrisostomo.github.io/fswatch/doc/1.14.0/fswatch.html/Tutorial-Introduction-to-fswatch.html#Detecting-File-System-Changes
# NOTE: We exclude bin/* to avoid infinite loop.
# TODO: Exclude *.sh, *.md, and other non-source files.
# FIXME: Sometimes fswatch fires a few times in a row. It is what it is.
fswatch -o . -e "bin/*" | (while read; do make install; date; echo; done)
fswatch -o . -e "bin/*" | (while read; echo "Building..."; do make install; date; echo; done)
2 changes: 2 additions & 0 deletions scripts/notary-start.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

set -e -x

NOTARY=$GOPATH/src/github.com/theupdateframework/notary

(cd $NOTARY; docker-compose up -d)
Expand Down
2 changes: 2 additions & 0 deletions scripts/signy-start.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

set -e -x

NOTARY=$GOPATH/src/github.com/theupdateframework/notary

(cd $NOTARY; docker-compose up -d)
Expand Down

0 comments on commit 6179f00

Please sign in to comment.