mirror of https://github.com/docker/docs.git
247 lines
6.1 KiB
Go
247 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"fmt"
|
|
"math"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/docker/notary/trustmanager"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
var subjectKeyID string
|
|
|
|
var cmdKeys = &cobra.Command{
|
|
Use: "keys",
|
|
Short: "Operates on keys.",
|
|
Long: "operations on signature keys and trusted certificate authorities.",
|
|
Run: keysList,
|
|
}
|
|
|
|
func init() {
|
|
cmdKeys.AddCommand(cmdKeysTrust)
|
|
cmdKeys.AddCommand(cmdKeysRemove)
|
|
cmdKeys.AddCommand(cmdKeysGenerate)
|
|
}
|
|
|
|
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 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) {
|
|
if len(args) < 1 {
|
|
cmd.Usage()
|
|
fatalf("must specify a SHA256 SubjectKeyID of the certificate")
|
|
}
|
|
|
|
//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
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// Ask for confirmation before removing keys
|
|
confirmed := askConfirm()
|
|
if !confirmed {
|
|
fatalf("aborting action.")
|
|
}
|
|
|
|
// Remove all the keys under the Global Unique Name
|
|
err = privKeyStore.RemoveDir(gunOrID)
|
|
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")
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
func keysList(cmd *cobra.Command, args []string) {
|
|
if len(args) > 0 {
|
|
cmd.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println("# Trusted CAs:")
|
|
trustedCAs := caStore.GetCertificates()
|
|
for _, c := range trustedCAs {
|
|
printCert(c)
|
|
}
|
|
|
|
fmt.Println("")
|
|
fmt.Println("# Trusted Certificates:")
|
|
trustedCerts := certificateStore.GetCertificates()
|
|
for _, c := range trustedCerts {
|
|
printCert(c)
|
|
}
|
|
|
|
fmt.Println("")
|
|
fmt.Println("# Signing keys: ")
|
|
for _, k := range privKeyStore.ListFiles(true) {
|
|
printKey(k)
|
|
}
|
|
}
|
|
|
|
func keysGenerate(cmd *cobra.Command, args []string) {
|
|
if len(args) < 1 {
|
|
cmd.Usage()
|
|
fatalf("must specify a GUN")
|
|
}
|
|
|
|
//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)
|
|
}
|
|
|
|
// _, cert, err := generateKeyAndCert(gun)
|
|
// if err != nil {
|
|
// fatalf("could not generate key: %v", err)
|
|
// }
|
|
|
|
// certificateStore.AddCert(cert)
|
|
// fingerprint := trustmanager.FingerprintCert(cert)
|
|
// fmt.Println("Generated new keypair with ID: ", fingerprint)
|
|
}
|
|
|
|
func printCert(cert *x509.Certificate) {
|
|
timeDifference := cert.NotAfter.Sub(time.Now())
|
|
certID, err := trustmanager.FingerprintCert(cert)
|
|
if err != nil {
|
|
fatalf("could not fingerprint certificate: %v", err)
|
|
}
|
|
|
|
fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24))
|
|
}
|
|
|
|
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:]
|
|
fmt.Printf("%s %s\n", gun, keyID)
|
|
}
|
|
|
|
func askConfirm() bool {
|
|
var res string
|
|
_, err := fmt.Scanln(&res)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if strings.EqualFold(res, "y") || strings.EqualFold(res, "yes") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|