Merge pull request #515 from docker/roles-for-targets

Roles for targets via notary CLI
This commit is contained in:
Diogo Mónica 2016-01-29 16:08:29 -08:00
commit 564f8d06d3
4 changed files with 185 additions and 9 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/docker/notary/server/storage"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
@ -50,6 +51,11 @@ func runCommand(t *testing.T, tempDir string, args ...string) (string, error) {
output, err := ioutil.ReadAll(b)
assert.NoError(t, err)
// Clean up state to mimic running a fresh command next time
for _, command := range cmd.Commands() {
command.ResetFlags()
}
return string(output), retErr
}
@ -362,6 +368,164 @@ func TestClientDelegationsInteraction(t *testing.T) {
assert.Contains(t, output, "No delegations present in this repository.")
}
// Initialize repo and test publishing targets with delegation roles
func TestClientDelegationsPublishing(t *testing.T) {
setUp(t)
tempDir := tempDirWithConfig(t, "{}")
defer os.RemoveAll(tempDir)
server := setupServer()
defer server.Close()
// Setup certificate for delegation role
tempFile, err := ioutil.TempFile("/tmp", "pemfile")
assert.NoError(t, err)
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, 2048)
assert.NoError(t, err)
privKeyBytesNoRole, err := trustmanager.KeyToPEM(privKey, "")
assert.NoError(t, err)
privKeyBytesWithRole, err := trustmanager.KeyToPEM(privKey, "user")
assert.NoError(t, err)
startTime := time.Now()
endTime := startTime.AddDate(10, 0, 0)
cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime)
assert.NoError(t, err)
_, err = tempFile.Write(trustmanager.CertToPEM(cert))
assert.NoError(t, err)
tempFile.Close()
defer os.Remove(tempFile.Name())
rawPubBytes, _ := ioutil.ReadFile(tempFile.Name())
parsedPubKey, _ := trustmanager.ParsePEMPublicKey(rawPubBytes)
canonicalKeyID, err := utils.CanonicalKeyID(parsedPubKey)
assert.NoError(t, err)
// Set up targets for publishing
tempTargetFile, err := ioutil.TempFile("/tmp", "targetfile")
assert.NoError(t, err)
tempTargetFile.Close()
defer os.Remove(tempTargetFile.Name())
var target = "sdgkadga"
var output string
// init repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
assert.NoError(t, err)
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list delegations - none yet
output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun")
assert.NoError(t, err)
assert.Contains(t, output, "No delegations present in this repository.")
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// validate that we have all keys, including snapshot
assertNumKeys(t, tempDir, 1, 2, true)
// rotate the snapshot key to server
output, err = runCommand(t, tempDir, "-s", server.URL, "key", "rotate", "gun", "-r", "--key-type", "snapshot")
assert.NoError(t, err)
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// validate that we lost the snapshot signing key
_, signingKeyIDs := assertNumKeys(t, tempDir, 1, 1, true)
targetKeyID := signingKeyIDs[0]
// add new valid delegation with single new cert
output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/releases", tempFile.Name(), "--paths", "\"\"")
assert.NoError(t, err)
assert.Contains(t, output, "Addition of delegation role")
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list delegations - we should see our one delegation
output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun")
assert.NoError(t, err)
assert.NotContains(t, output, "No delegations present in this repository.")
// remove the targets key to demonstrate that delegates don't need this key
keyDir := filepath.Join(tempDir, "private", "tuf_keys")
assert.NoError(t, os.Remove(filepath.Join(keyDir, "gun", targetKeyID+".key")))
// Note that we need to use the canonical key ID, followed by the base of the role here
err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+"_releases.key"), privKeyBytesNoRole, 0700)
assert.NoError(t, err)
// add a target using the delegation -- will only add to targets/releases
_, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases")
assert.NoError(t, err)
// list targets for targets/releases - we should see no targets until we publish
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases")
assert.NoError(t, err)
assert.Contains(t, output, "No targets")
output, err = runCommand(t, tempDir, "-s", server.URL, "status", "gun")
assert.NoError(t, err)
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list targets for targets/releases - we should see our target!
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases")
assert.NoError(t, err)
assert.Contains(t, output, "targets/releases")
// remove the target for this role only
_, err = runCommand(t, tempDir, "remove", "gun", target, "--roles", "targets/releases")
assert.NoError(t, err)
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list targets for targets/releases - we should see no targets
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases")
assert.NoError(t, err)
assert.Contains(t, output, "No targets present")
// Try adding a target with a different key style - private/tuf_keys/canonicalKeyID.key with "user" set as the "role" PEM header
// First remove the old key and add the new style
assert.NoError(t, os.Remove(filepath.Join(keyDir, canonicalKeyID+"_releases.key")))
err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+".key"), privKeyBytesWithRole, 0700)
assert.NoError(t, err)
// add a target using the delegation -- will only add to targets/releases
_, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases")
assert.NoError(t, err)
// list targets for targets/releases - we should see no targets until we publish
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases")
assert.NoError(t, err)
assert.Contains(t, output, "No targets")
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list targets for targets/releases - we should see our target!
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases")
assert.NoError(t, err)
assert.Contains(t, output, "targets/releases")
}
// Splits a string into lines, and returns any lines that are not empty (
// striped of whitespace)
func splitLines(chunk string) []string {

View File

@ -22,6 +22,7 @@ const (
var (
debug bool
verbose bool
roles []string
trustDir string
configFile string
remoteTrustServer string
@ -127,8 +128,11 @@ func setupCommand(notaryCmd *cobra.Command) {
notaryCmd.AddCommand(cmdDelegationGenerator.GetCommand())
notaryCmd.AddCommand(cmdCert)
notaryCmd.AddCommand(cmdTufInit)
cmdTufList.Flags().StringSliceVarP(&roles, "roles", "r", nil, "Delegation roles to list targets for (will shadow targets role)")
notaryCmd.AddCommand(cmdTufList)
cmdTufAdd.Flags().StringSliceVarP(&roles, "roles", "r", nil, "Delegation roles to add this target to")
notaryCmd.AddCommand(cmdTufAdd)
cmdTufRemove.Flags().StringSliceVarP(&roles, "roles", "r", nil, "Delegation roles to remove this target from")
notaryCmd.AddCommand(cmdTufRemove)
notaryCmd.AddCommand(cmdTufStatus)
notaryCmd.AddCommand(cmdTufPublish)

View File

@ -104,7 +104,8 @@ func tufAdd(cmd *cobra.Command, args []string) {
if err != nil {
fatalf(err.Error())
}
err = nRepo.AddTarget(target)
// If roles is empty, we default to adding to targets
err = nRepo.AddTarget(target, roles...)
if err != nil {
fatalf(err.Error())
}
@ -163,8 +164,9 @@ func tufList(cmd *cobra.Command, args []string) {
fatalf(err.Error())
}
// Retreive the remote list of signed targets
targetList, err := nRepo.ListTargets(data.CanonicalTargetsRole, "targets/releases")
// Retrieve the remote list of signed targets, prioritizing the passed-in list over targets
roles = append(roles, data.CanonicalTargetsRole)
targetList, err := nRepo.ListTargets(roles...)
if err != nil {
fatalf(err.Error())
}
@ -265,7 +267,8 @@ func tufRemove(cmd *cobra.Command, args []string) {
if err != nil {
fatalf(err.Error())
}
err = repo.RemoveTarget(targetName)
// If roles is empty, we default to removing from targets
err = repo.RemoveTarget(targetName, roles...)
if err != nil {
fatalf(err.Error())
}

View File

@ -470,12 +470,17 @@ func KeyToPEM(privKey data.PrivateKey, role string) ([]byte, error) {
return nil, err
}
block := &pem.Block{
Type: bt,
Headers: map[string]string{
headers := map[string]string{}
if role != "" {
headers = map[string]string{
"role": role,
},
Bytes: privKey.Private(),
}
}
block := &pem.Block{
Type: bt,
Headers: headers,
Bytes: privKey.Private(),
}
return pem.EncodeToMemory(block), nil