Removed all local keystores, added configurable trust dir

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-27 22:13:16 -07:00
parent 0d03d8a8e5
commit 579f51866b
3 changed files with 131 additions and 239 deletions

View File

@ -4,104 +4,63 @@ import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"math" "math"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/docker/notary/keystoremanager"
"github.com/docker/notary/trustmanager" "github.com/docker/notary/trustmanager"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper"
) )
var subjectKeyID string func init() {
cmdKeys.AddCommand(cmdKeysRemoveRootKey)
cmdKeys.AddCommand(cmdKeysGenerateRootKey)
}
var cmdKeys = &cobra.Command{ var cmdKeys = &cobra.Command{
Use: "keys", Use: "keys",
Short: "Operates on keys.", Short: "Operates on root keys.",
Long: "operations on signature keys and trusted certificate authorities.", Long: "operations on private root keys.",
Run: keysList, Run: keysList,
} }
func init() { var cmdKeysRemoveRootKey = &cobra.Command{
cmdKeys.AddCommand(cmdKeysTrust) Use: "remove [ keyID ]",
cmdKeys.AddCommand(cmdKeysRemove) Short: "Removes the root key with the given keyID.",
cmdKeys.AddCommand(cmdKeysGenerate) Long: "remove the root key with the given keyID from the local host.",
Run: keysRemoveRootKey,
} }
var cmdKeysRemove = &cobra.Command{ var cmdKeysGenerateRootKey = &cobra.Command{
Use: "remove [ Subject Key ID ]", Use: "generate [ algorithm ]",
Short: "Removes trust from a specific certificate authority or certificate.", Short: "Generates a new root key with a given algorithm.",
Long: "remove trust from a specific certificate authority.", Long: "generates a new root key with a given algorithm.",
Run: keysRemove, Run: keysGenerateRootKey,
} }
var cmdKeysTrust = &cobra.Command{ // keysRemoveRootKey deletes a root private key based on ID
Use: "trust [ certificate ]", func keysRemoveRootKey(cmd *cobra.Command, args []string) {
Short: "Trusts a new certificate.",
Long: "adds a the certificate to the trusted certificate authority list.",
Run: keysTrust,
}
var cmdKeysGenerate = &cobra.Command{
Use: "generate [ GUN ]",
Short: "Generates a new key for a specific GUN.",
Long: "generates a new key for a specific Global Unique Name.",
Run: keysGenerate,
}
// keysRemove deletes Certificates based on hash and Private Keys
// based on GUNs.
func keysRemove(cmd *cobra.Command, args []string) {
if len(args) < 1 { if len(args) < 1 {
cmd.Usage() cmd.Usage()
fatalf("must specify a SHA256 SubjectKeyID of the certificate") fatalf("must specify the key ID of the root key to remove")
} }
//TODO (diogo): Validate Global Unique Name. We probably want to reject 1 char GUNs. keyID := args[0]
gunOrID := args[0] if len(keyID) != 64 {
fatalf("please enter a valid root key ID")
// Try to retrieve the ID from the CA store.
cert, err := caStore.GetCertificateByCertID(gunOrID)
if err == nil {
fmt.Printf("Removing: ")
printCert(cert)
// If the ID is found, remove it.
err = caStore.RemoveCert(cert)
if err != nil {
fatalf("failed to remove certificate from KeyStore")
}
return
} }
parseConfig()
// Try to retrieve the ID from the Certificate store. keyStoreManager, err := keystoremanager.NewKeyStoreManager(TrustDir, retriever)
cert, err = certificateStore.GetCertificateByCertID(gunOrID) if err != nil {
if err == nil { fatalf("failed to create a new truststore manager with directory: %s", TrustDir)
fmt.Printf("Removing: ")
printCert(cert)
// If the ID is found, remove it.
err = certificateStore.RemoveCert(cert)
if err != nil {
fatalf("failed to remove certificate from KeyStore")
}
return
}
// We didn't find a certificate with this ID, let's try to see if we can find keys.
keyList := privKeyStore.ListDir(gunOrID, true)
if len(keyList) < 1 {
fatalf("no Private Keys found under Global Unique Name: %s", gunOrID)
} }
// List all the keys about to be removed // List all the keys about to be removed
fmt.Println("Are you sure you want to remove the following keys? (yes/no)", gunOrID) fmt.Printf("Are you sure you want to remove the following key? (yes/no)\n%s\n", keyID)
for _, k := range keyList {
printKey(k)
}
// Ask for confirmation before removing keys // Ask for confirmation before removing keys
confirmed := askConfirm() confirmed := askConfirm()
@ -110,60 +69,12 @@ func keysRemove(cmd *cobra.Command, args []string) {
} }
// Remove all the keys under the Global Unique Name // Remove all the keys under the Global Unique Name
err = privKeyStore.RemoveDir(gunOrID) err = keyStoreManager.RootKeyStore().RemoveKey(keyID)
if err != nil { if err != nil {
fatalf("failed to remove all Private keys under Global Unique Name: %s", gunOrID) fatalf("failed to remove root key with key ID: %s", keyID)
}
fmt.Printf("Removing all Private keys from: %s \n", gunOrID)
}
//TODO (diogo): Ask the use if she wants to trust the GUN in the cert
func keysTrust(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cmd.Usage()
fatalf("please provide a URL or filename to a certificate")
} }
certLocationStr := args[0] fmt.Printf("Root key %s removed\n", keyID)
var cert *x509.Certificate
// Verify if argument is a valid URL
url, err := url.Parse(certLocationStr)
if err == nil && url.Scheme != "" {
cert, err = trustmanager.GetCertFromURL(certLocationStr)
if err != nil {
fatalf("error retrieving certificate from url (%s): %v", certLocationStr, err)
}
} else if _, err := os.Stat(certLocationStr); err == nil {
// Try to load the certificate from the file
cert, err = trustmanager.LoadCertFromFile(certLocationStr)
if err != nil {
fatalf("error adding certificate from file: %v", err)
}
} else {
fatalf("please provide a file location or URL for CA certificate.")
}
// Ask for confirmation before adding certificate into repository
fmt.Printf("Are you sure you want to add trust for: %s? (yes/no)\n", cert.Subject.CommonName)
confirmed := askConfirm()
if !confirmed {
fatalf("aborting action.")
}
err = nil
if cert.IsCA {
err = caStore.AddCert(cert)
} else {
err = certificateStore.AddCert(cert)
}
if err != nil {
fatalf("error adding certificate from file: %v", err)
}
fmt.Printf("Adding: ")
printCert(cert)
} }
func keysList(cmd *cobra.Command, args []string) { func keysList(cmd *cobra.Command, args []string) {
@ -172,46 +83,62 @@ func keysList(cmd *cobra.Command, args []string) {
os.Exit(1) os.Exit(1)
} }
fmt.Println("# Trusted CAs:") parseConfig()
trustedCAs := caStore.GetCertificates()
for _, c := range trustedCAs { keyStoreManager, err := keystoremanager.NewKeyStoreManager(TrustDir, retriever)
printCert(c) if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", TrustDir)
} }
fmt.Println("") fmt.Println("")
fmt.Println("# Trusted Certificates:") fmt.Println("# Trusted Certificates:")
trustedCerts := certificateStore.GetCertificates() trustedCerts := keyStoreManager.TrustedCertificateStore().GetCertificates()
for _, c := range trustedCerts { for _, c := range trustedCerts {
printCert(c) printCert(c)
} }
fmt.Println("")
fmt.Println("# Root keys: ")
for _, k := range keyStoreManager.RootKeyStore().ListKeys() {
fmt.Println(k)
}
fmt.Println("") fmt.Println("")
fmt.Println("# Signing keys: ") fmt.Println("# Signing keys: ")
for _, k := range privKeyStore.ListFiles(true) { for _, k := range keyStoreManager.NonRootKeyStore().ListKeys() {
printKey(k) printKey(k)
} }
} }
func keysGenerate(cmd *cobra.Command, args []string) { func keysGenerateRootKey(cmd *cobra.Command, args []string) {
if len(args) < 1 { if len(args) < 1 {
cmd.Usage() cmd.Usage()
fatalf("must specify a GUN") fatalf("must specify an Algorithm (RSA, ECDSA)")
} }
//TODO (diogo): Validate GUNs. Don't allow '/' or '\' for now. algorithm := args[0]
gun := args[0] allowedCiphers := map[string]bool{
if gun[0:1] == "/" || gun[0:1] == "\\" { "rsa": true,
fatalf("invalid Global Unique Name: %s", gun) "ecdsa": true,
} }
// _, cert, err := generateKeyAndCert(gun) if !allowedCiphers[strings.ToLower(algorithm)] {
// if err != nil { fatalf("algorithm not allowed, possible values are: RSA, ECDSA")
// fatalf("could not generate key: %v", err) }
// }
// certificateStore.AddCert(cert) parseConfig()
// fingerprint := trustmanager.FingerprintCert(cert)
// fmt.Println("Generated new keypair with ID: ", fingerprint) keyStoreManager, err := keystoremanager.NewKeyStoreManager(TrustDir, retriever)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", TrustDir)
}
keyID, err := keyStoreManager.GenRootKey(algorithm)
if err != nil {
fatalf("failed to create a new root key: %v", err)
}
fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, keyID)
} }
func printCert(cert *x509.Certificate) { func printCert(cert *x509.Certificate) {
@ -225,11 +152,8 @@ func printCert(cert *x509.Certificate) {
} }
func printKey(keyPath string) { func printKey(keyPath string) {
keyPath = strings.TrimSuffix(keyPath, filepath.Ext(keyPath))
keyPath = strings.TrimPrefix(keyPath, viper.GetString("privDir"))
keyID := filepath.Base(keyPath) keyID := filepath.Base(keyPath)
gun := filepath.Dir(keyPath)[1:] gun := filepath.Dir(keyPath)
fmt.Printf("%s %s\n", gun, keyID) fmt.Printf("%s %s\n", gun, keyID)
} }

View File

@ -1,96 +1,71 @@
package main package main
import ( import (
"crypto/x509"
"fmt" "fmt"
"os" "os"
"os/user" "os/user"
"path" "path/filepath"
"time"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/docker/notary/trustmanager" "github.com/docker/notary/pkg/passphrase"
) )
const configFileName string = "config" const configFileName string = "config"
const configPath string = ".docker/trust/" const defaultTrustDir string = ".notary/"
const trustDir string = "trusted_certificates/" const defaultServerURL = "https://notary-server:4443"
const privDir string = "private/"
const rootKeysDir string = "root_keys/"
var rawOutput bool var rawOutput bool
var caStore trustmanager.X509Store var TrustDir string
var certificateStore trustmanager.X509Store var RemoteTrustServer string
var privKeyStore trustmanager.FileStore var Verbose bool
var retriever passphrase.Retriever
func init() { func init() {
logrus.SetLevel(logrus.DebugLevel) retriever = passphrase.PromptRetriever()
logrus.SetOutput(os.Stderr) }
// Retrieve current user to get home directory
usr, err := user.Current() func parseConfig() {
if err != nil { if Verbose {
fatalf("cannot get current user: %v", err) logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stderr)
} }
// Get home directory for current user if TrustDir == "" {
homeDir := usr.HomeDir // Retrieve current user to get home directory
if homeDir == "" { usr, err := user.Current()
fatalf("cannot get current user home directory") if err != nil {
fatalf("cannot get current user: %v", err)
}
// Get home directory for current user
homeDir := usr.HomeDir
if homeDir == "" {
fatalf("cannot get current user home directory")
}
TrustDir = filepath.Join(homeDir, filepath.Dir(defaultTrustDir))
logrus.Debugf("no trust directory provided, using default: %s", TrustDir)
} else {
logrus.Debugf("trust directory provided: %s", TrustDir)
} }
// Setup the configuration details // Setup the configuration details
viper.SetConfigName(configFileName) viper.SetConfigName(configFileName)
viper.AddConfigPath(path.Join(homeDir, path.Dir(configPath))) viper.AddConfigPath(TrustDir)
viper.SetConfigType("json") viper.SetConfigType("json")
// Find and read the config file // Find and read the config file
err = viper.ReadInConfig() err := viper.ReadInConfig()
if err != nil { if err != nil {
logrus.Debugf("configuration file not found, using defaults")
// Ignore if the configuration file doesn't exist, we can use the defaults // Ignore if the configuration file doesn't exist, we can use the defaults
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
fatalf("fatal error config file: %v", err) fatalf("fatal error config file: %v", err)
} }
} }
// Set up the defaults for our config
viper.SetDefault("baseTrustDir", path.Join(homeDir, path.Dir(configPath)))
// Get the final value for the CA directory
finalTrustDir := path.Join(viper.GetString("baseTrustDir"), trustDir)
finalPrivDir := path.Join(viper.GetString("baseTrustDir"), privDir)
// Load all CAs that aren't expired and don't use SHA1
caStore, err = trustmanager.NewX509FilteredFileStore(finalTrustDir, func(cert *x509.Certificate) bool {
return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil &&
time.Now().Before(cert.NotAfter) &&
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
})
if err != nil {
fatalf("could not create CA X509FileStore: %v", err)
}
// Load all individual (nonCA) certificates that aren't expired and don't use SHA1
certificateStore, err = trustmanager.NewX509FilteredFileStore(finalTrustDir, func(cert *x509.Certificate) bool {
return !cert.IsCA &&
time.Now().Before(cert.NotAfter) &&
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
})
if err != nil {
fatalf("could not create Certificate X509FileStore: %v", err)
}
privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir,
func(string, string, bool, int) (string, bool, error) { return "", false, nil })
if err != nil {
fatalf("could not create KeyFileStore: %v", err)
}
} }
func main() { func main() {
@ -100,17 +75,22 @@ func main() {
Long: "notary allows the creation and management of collections of signed targets, allowing the signing and validation of arbitrary content.", Long: "notary allows the creation and management of collections of signed targets, allowing the signing and validation of arbitrary content.",
} }
NotaryCmd.PersistentFlags().StringVarP(&TrustDir, "trustdir", "d", "", "directory where the trust data is persisted to")
NotaryCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
NotaryCmd.AddCommand(cmdKeys) NotaryCmd.AddCommand(cmdKeys)
NotaryCmd.AddCommand(cmdTufInit) NotaryCmd.AddCommand(cmdTufInit)
cmdTufInit.Flags().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
NotaryCmd.AddCommand(cmdTufList) NotaryCmd.AddCommand(cmdTufList)
cmdTufList.Flags().BoolVarP(&rawOutput, "raw", "", false, "Instructs notary list to output a nonpretty printed version of the targets list. Useful if you need to parse the list.") cmdTufList.Flags().BoolVarP(&rawOutput, "raw", "", false, "Instructs notary list to output a nonpretty printed version of the targets list. Useful if you need to parse the list.")
cmdTufList.Flags().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
NotaryCmd.AddCommand(cmdTufAdd) NotaryCmd.AddCommand(cmdTufAdd)
NotaryCmd.AddCommand(cmdTufRemove) NotaryCmd.AddCommand(cmdTufRemove)
NotaryCmd.AddCommand(cmdTufPublish) NotaryCmd.AddCommand(cmdTufPublish)
cmdTufPublish.Flags().StringVarP(&remoteTrustServer, "remote", "r", "", "Remote trust server location") cmdTufPublish.Flags().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
NotaryCmd.AddCommand(cmdTufLookup) NotaryCmd.AddCommand(cmdTufLookup)
cmdTufLookup.Flags().BoolVarP(&rawOutput, "raw", "", false, "Instructs notary lookup to output a nonpretty printed version of the targets list. Useful if you need to parse the list.") cmdTufLookup.Flags().BoolVarP(&rawOutput, "raw", "", false, "Instructs notary lookup to output a nonpretty printed version of the targets list. Useful if you need to parse the list.")
cmdTufLookup.Flags().StringVarP(&remoteTrustServer, "remote", "r", "", "Remote trust server location") cmdTufLookup.Flags().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
NotaryCmd.AddCommand(cmdVerify) NotaryCmd.AddCommand(cmdVerify)
NotaryCmd.Execute() NotaryCmd.Execute()

View File

@ -9,24 +9,13 @@ import (
"os" "os"
"crypto/subtle" "crypto/subtle"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
notaryclient "github.com/docker/notary/client" notaryclient "github.com/docker/notary/client"
"github.com/docker/notary/pkg/passphrase"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// FIXME: This should not be hardcoded
const hardcodedBaseURL = "http://notary-server:4443"
var retriever passphrase.Retriever
func init() {
retriever = passphrase.PromptRetriever()
}
var remoteTrustServer string
var cmdTufList = &cobra.Command{ var cmdTufList = &cobra.Command{
Use: "list [ GUN ]", Use: "list [ GUN ]",
Short: "Lists targets for a trusted collection.", Short: "Lists targets for a trusted collection.",
@ -86,8 +75,9 @@ func tufAdd(cmd *cobra.Command, args []string) {
targetName := args[1] targetName := args[1]
targetPath := args[2] targetPath := args[2]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, parseConfig()
getTransport(), retriever)
nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -96,7 +86,7 @@ func tufAdd(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
err = repo.AddTarget(target) err = nRepo.AddTarget(target)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -110,9 +100,9 @@ func tufInit(cmd *cobra.Command, args []string) {
} }
gun := args[0] gun := args[0]
parseConfig()
nRepo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -147,14 +137,15 @@ func tufList(cmd *cobra.Command, args []string) {
fatalf("must specify a GUN") fatalf("must specify a GUN")
} }
gun := args[0] gun := args[0]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, parseConfig()
getTransport(), retriever)
nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
// Retreive the remote list of signed targets // Retreive the remote list of signed targets
targetList, err := repo.ListTargets() targetList, err := nRepo.ListTargets()
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -172,15 +163,14 @@ func tufLookup(cmd *cobra.Command, args []string) {
} }
gun := args[0] gun := args[0]
targetName := args[1] targetName := args[1]
parseConfig()
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
// TODO(diogo): Parse Targets and print them target, err := nRepo.GetTargetByName(targetName)
target, err := repo.GetTargetByName(targetName)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -195,16 +185,16 @@ func tufPublish(cmd *cobra.Command, args []string) {
} }
gun := args[0] gun := args[0]
parseConfig()
fmt.Println("Pushing changes to ", gun, ".") fmt.Println("Pushing changes to ", gun, ".")
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
err = repo.Publish() err = nRepo.Publish()
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
@ -217,8 +207,9 @@ func tufRemove(cmd *cobra.Command, args []string) {
} }
gun := args[0] gun := args[0]
targetName := args[1] targetName := args[1]
parseConfig()
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, remoteTrustServer,
getTransport(), retriever) getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
@ -234,25 +225,22 @@ func verify(cmd *cobra.Command, args []string) {
cmd.Usage() cmd.Usage()
fatalf("must specify a GUN and target") fatalf("must specify a GUN and target")
} }
parseConfig()
// Reads all of the data on STDIN // Reads all of the data on STDIN
//TODO (diogo): Change this to do a streaming hash
payload, err := ioutil.ReadAll(os.Stdin) payload, err := ioutil.ReadAll(os.Stdin)
if err != nil { if err != nil {
fatalf("error reading content from STDIN: %v", err) fatalf("error reading content from STDIN: %v", err)
} }
//TODO (diogo): This code is copy/pasted from lookup.
gun := args[0] gun := args[0]
targetName := args[1] targetName := args[1]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, nRepo, err := notaryclient.NewNotaryRepository(TrustDir, gun, RemoteTrustServer, getTransport(), retriever)
getTransport(), retriever)
if err != nil { if err != nil {
fatalf(err.Error()) fatalf(err.Error())
} }
// TODO(diogo): Parse Targets and print them target, err := nRepo.GetTargetByName(targetName)
target, err := repo.GetTargetByName(targetName)
if err != nil { if err != nil {
logrus.Error("notary: data not present in the trusted collection.") logrus.Error("notary: data not present in the trusted collection.")
os.Exit(-11) os.Exit(-11)