mirror of https://github.com/docker/docs.git
				
				
				
			Merge pull request #515 from docker/roles-for-targets
Roles for targets via notary CLI
This commit is contained in:
		
						commit
						564f8d06d3
					
				| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue