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