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"
"fmt"
"math"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/docker/notary/keystoremanager"
"github.com/docker/notary/trustmanager"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var subjectKeyID string
func init() {
cmdKeys.AddCommand(cmdKeysRemoveRootKey)
cmdKeys.AddCommand(cmdKeysGenerateRootKey)
}
var cmdKeys = &cobra.Command{
Use: "keys",
Short: "Operates on keys.",
Long: "operations on signature keys and trusted certificate authorities.",
Short: "Operates on root keys.",
Long: "operations on private root keys.",
Run: keysList,
}
func init() {
cmdKeys.AddCommand(cmdKeysTrust)
cmdKeys.AddCommand(cmdKeysRemove)
cmdKeys.AddCommand(cmdKeysGenerate)
var cmdKeysRemoveRootKey = &cobra.Command{
Use: "remove [ keyID ]",
Short: "Removes the root key with the given keyID.",
Long: "remove the root key with the given keyID from the local host.",
Run: keysRemoveRootKey,
}
var cmdKeysRemove = &cobra.Command{
Use: "remove [ Subject Key ID ]",
Short: "Removes trust from a specific certificate authority or certificate.",
Long: "remove trust from a specific certificate authority.",
Run: keysRemove,
var cmdKeysGenerateRootKey = &cobra.Command{
Use: "generate [ algorithm ]",
Short: "Generates a new root key with a given algorithm.",
Long: "generates a new root key with a given algorithm.",
Run: keysGenerateRootKey,
}
var cmdKeysTrust = &cobra.Command{
Use: "trust [ certificate ]",
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) {
// keysRemoveRootKey deletes a root private key based on ID
func keysRemoveRootKey(cmd *cobra.Command, args []string) {
if len(args) < 1 {
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.
gunOrID := args[0]
// 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
keyID := args[0]
if len(keyID) != 64 {
fatalf("please enter a valid root key ID")
}
parseConfig()
// Try to retrieve the ID from the Certificate store.
cert, err = certificateStore.GetCertificateByCertID(gunOrID)
if err == nil {
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)
keyStoreManager, err := keystoremanager.NewKeyStoreManager(TrustDir, retriever)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", TrustDir)
}
// List all the keys about to be removed
fmt.Println("Are you sure you want to remove the following keys? (yes/no)", gunOrID)
for _, k := range keyList {
printKey(k)
}
fmt.Printf("Are you sure you want to remove the following key? (yes/no)\n%s\n", keyID)
// Ask for confirmation before removing keys
confirmed := askConfirm()
@ -110,60 +69,12 @@ func keysRemove(cmd *cobra.Command, args []string) {
}
// Remove all the keys under the Global Unique Name
err = privKeyStore.RemoveDir(gunOrID)
err = keyStoreManager.RootKeyStore().RemoveKey(keyID)
if err != nil {
fatalf("failed to remove all Private keys under Global Unique Name: %s", gunOrID)
}
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")
fatalf("failed to remove root key with key ID: %s", keyID)
}
certLocationStr := args[0]
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)
fmt.Printf("Root key %s removed\n", keyID)
}
func keysList(cmd *cobra.Command, args []string) {
@ -172,46 +83,62 @@ func keysList(cmd *cobra.Command, args []string) {
os.Exit(1)
}
fmt.Println("# Trusted CAs:")
trustedCAs := caStore.GetCertificates()
for _, c := range trustedCAs {
printCert(c)
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(TrustDir, retriever)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", TrustDir)
}
fmt.Println("")
fmt.Println("# Trusted Certificates:")
trustedCerts := certificateStore.GetCertificates()
trustedCerts := keyStoreManager.TrustedCertificateStore().GetCertificates()
for _, c := range trustedCerts {
printCert(c)
}
fmt.Println("")
fmt.Println("# Root keys: ")
for _, k := range keyStoreManager.RootKeyStore().ListKeys() {
fmt.Println(k)
}
fmt.Println("")
fmt.Println("# Signing keys: ")
for _, k := range privKeyStore.ListFiles(true) {
for _, k := range keyStoreManager.NonRootKeyStore().ListKeys() {
printKey(k)
}
}
func keysGenerate(cmd *cobra.Command, args []string) {
func keysGenerateRootKey(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cmd.Usage()
fatalf("must specify a GUN")
fatalf("must specify an Algorithm (RSA, ECDSA)")
}
//TODO (diogo): Validate GUNs. Don't allow '/' or '\' for now.
gun := args[0]
if gun[0:1] == "/" || gun[0:1] == "\\" {
fatalf("invalid Global Unique Name: %s", gun)
algorithm := args[0]
allowedCiphers := map[string]bool{
"rsa": true,
"ecdsa": true,
}
// _, cert, err := generateKeyAndCert(gun)
// if err != nil {
// fatalf("could not generate key: %v", err)
// }
if !allowedCiphers[strings.ToLower(algorithm)] {
fatalf("algorithm not allowed, possible values are: RSA, ECDSA")
}
// certificateStore.AddCert(cert)
// fingerprint := trustmanager.FingerprintCert(cert)
// fmt.Println("Generated new keypair with ID: ", fingerprint)
parseConfig()
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) {
@ -225,11 +152,8 @@ func printCert(cert *x509.Certificate) {
}
func printKey(keyPath string) {
keyPath = strings.TrimSuffix(keyPath, filepath.Ext(keyPath))
keyPath = strings.TrimPrefix(keyPath, viper.GetString("privDir"))
keyID := filepath.Base(keyPath)
gun := filepath.Dir(keyPath)[1:]
gun := filepath.Dir(keyPath)
fmt.Printf("%s %s\n", gun, keyID)
}

View File

@ -1,96 +1,71 @@
package main
import (
"crypto/x509"
"fmt"
"os"
"os/user"
"path"
"time"
"path/filepath"
"github.com/Sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/pkg/passphrase"
)
const configFileName string = "config"
const configPath string = ".docker/trust/"
const trustDir string = "trusted_certificates/"
const privDir string = "private/"
const rootKeysDir string = "root_keys/"
const defaultTrustDir string = ".notary/"
const defaultServerURL = "https://notary-server:4443"
var rawOutput bool
var caStore trustmanager.X509Store
var certificateStore trustmanager.X509Store
var privKeyStore trustmanager.FileStore
var TrustDir string
var RemoteTrustServer string
var Verbose bool
var retriever passphrase.Retriever
func init() {
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stderr)
// Retrieve current user to get home directory
usr, err := user.Current()
if err != nil {
fatalf("cannot get current user: %v", err)
retriever = passphrase.PromptRetriever()
}
func parseConfig() {
if Verbose {
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stderr)
}
// Get home directory for current user
homeDir := usr.HomeDir
if homeDir == "" {
fatalf("cannot get current user home directory")
if TrustDir == "" {
// Retrieve current user to get home directory
usr, err := user.Current()
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
viper.SetConfigName(configFileName)
viper.AddConfigPath(path.Join(homeDir, path.Dir(configPath)))
viper.AddConfigPath(TrustDir)
viper.SetConfigType("json")
// Find and read the config file
err = viper.ReadInConfig()
err := viper.ReadInConfig()
if err != nil {
logrus.Debugf("configuration file not found, using defaults")
// Ignore if the configuration file doesn't exist, we can use the defaults
if !os.IsNotExist(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() {
@ -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.",
}
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(cmdTufInit)
cmdTufInit.Flags().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
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().StringVarP(&RemoteTrustServer, "server", "s", defaultServerURL, "Remote trust server location")
NotaryCmd.AddCommand(cmdTufAdd)
NotaryCmd.AddCommand(cmdTufRemove)
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)
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.Execute()

View File

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