Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support managing plugin keys #168

Merged
merged 14 commits into from
May 18, 2022
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
go-version: [1.17]
go-version: [1.18]
fail-fast: true
steps:
- name: Set up Go ${{ matrix.go-version }}
Expand Down
42 changes: 12 additions & 30 deletions cmd/notation/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"time"

"github.com/notaryproject/notation-go/crypto/cryptoutil"
"github.com/notaryproject/notation/internal/ioutil"
"github.com/notaryproject/notation/internal/slices"
"github.com/notaryproject/notation/pkg/config"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -96,9 +99,6 @@ func addCert(ctx *cli.Context) error {
return err
}
name := ctx.String("name")
if name == "" {
name = nameFromPath(path)
}

// check if the target path is a cert
if _, err := cryptoutil.ReadCertificateFile(path); err != nil {
Expand All @@ -123,9 +123,13 @@ func addCert(ctx *cli.Context) error {
}

func addCertCore(cfg *config.File, name, path string) error {
if ok := cfg.VerificationCertificates.Certificates.Append(name, path); !ok {
if slices.Contains(cfg.VerificationCertificates.Certificates, name) {
return errors.New(name + ": already exists")
}
cfg.VerificationCertificates.Certificates = append(cfg.VerificationCertificates.Certificates, config.CertificateReference{
Name: name,
Path: path,
})
return nil
}

Expand All @@ -137,8 +141,7 @@ func listCerts(ctx *cli.Context) error {
}

// write out
printCertificateSet(cfg.VerificationCertificates.Certificates)
return nil
return ioutil.PrintCertificateMap(os.Stdout, cfg.VerificationCertificates.Certificates)
}

func removeCerts(ctx *cli.Context) error {
Expand All @@ -156,9 +159,11 @@ func removeCerts(ctx *cli.Context) error {

var removedNames []string
for _, name := range names {
if ok := cfg.VerificationCertificates.Certificates.Remove(name); !ok {
idx := slices.Index(cfg.VerificationCertificates.Certificates, name)
if idx < 0 {
return errors.New(name + ": not found")
}
cfg.VerificationCertificates.Certificates = slices.Delete(cfg.VerificationCertificates.Certificates, idx)
removedNames = append(removedNames, name)
}
if err := cfg.Save(); err != nil {
Expand All @@ -171,26 +176,3 @@ func removeCerts(ctx *cli.Context) error {
}
return nil
}

func printCertificateSet(s config.CertificateMap) {
maxNameSize := 0
for _, ref := range s {
if len(ref.Name) > maxNameSize {
maxNameSize = len(ref.Name)
}
}
format := fmt.Sprintf("%%-%ds\t%%s\n", maxNameSize)
fmt.Printf(format, "NAME", "PATH")
for _, ref := range s {
fmt.Printf(format, ref.Name, ref.Path)
}
}

func nameFromPath(path string) string {
base := filepath.Base(path)
name := base[:len(base)-len(filepath.Ext(base))]
if name == "" {
return base
}
return name
}
6 changes: 4 additions & 2 deletions cmd/notation/cert_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ func generateTestCert(ctx *cli.Context) error {
if err != nil {
return err
}
isDefaultKey, err := addKeyCore(cfg, name, keyPath, certPath, ctx.Bool(keyDefaultFlag.Name))
isDefault := ctx.Bool(keyDefaultFlag.Name)
keySuite := config.KeySuite{Name: name, X509KeyPair: &config.X509KeyPair{KeyPath: keyPath, CertificatePath: certPath}}
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
err = addKeyCore(cfg, keySuite, ctx.Bool(keyDefaultFlag.Name))
if err != nil {
return err
}
Expand All @@ -79,7 +81,7 @@ func generateTestCert(ctx *cli.Context) error {

// write out
fmt.Printf("%s: added to the key list\n", name)
if isDefaultKey {
if isDefault {
fmt.Printf("%s: marked as default\n", name)
}
if trust {
Expand Down
155 changes: 89 additions & 66 deletions cmd/notation/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import (
"crypto/tls"
"errors"
"fmt"
"os"
"path/filepath"

"github.com/notaryproject/notation-go/plugin/manager"
"github.com/notaryproject/notation/internal/ioutil"
"github.com/notaryproject/notation/internal/slices"
"github.com/notaryproject/notation/pkg/config"
"github.com/urfave/cli/v2"
)
Expand All @@ -31,12 +35,22 @@ var (
keyAddCommand = &cli.Command{
Name: "add",
Usage: "Add key to signing key list",
ArgsUsage: "<key_path> <cert_path>",
ArgsUsage: "[<key_path> <cert_path>]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "key name",
Name: "name",
Aliases: []string{"n"},
Usage: "key name",
Required: true,
},
&cli.StringFlag{
Name: "plugin",
Aliases: []string{"p"},
Usage: "signing plugin name",
},
&cli.StringFlag{
Name: "id",
Usage: "key id (required if --plugin is set)",
},
keyDefaultFlag,
},
Expand Down Expand Up @@ -71,63 +85,97 @@ var (
)

func addKey(ctx *cli.Context) error {
// initialize
args := ctx.Args()
switch args.Len() {
case 0:
return errors.New("missing key and certificate paths")
case 1:
return errors.New("missing certificate path for the correspoding key")
}

keyPath, err := filepath.Abs(args.Get(0))
if err != nil {
return err
}
certPath, err := filepath.Abs(args.Get(1))
cfg, err := config.LoadOrDefault()
if err != nil {
return err
}
var key config.KeySuite
pluginName := ctx.String("plugin")
name := ctx.String("name")
if name == "" {
name = nameFromPath(keyPath)
}

// check key / cert pair
if _, err := tls.LoadX509KeyPair(certPath, keyPath); err != nil {
return err
if pluginName != "" {
key, err = addExternalKey(ctx, pluginName, name)
} else {
key, err = newX509KeyPair(ctx, name)
}

// core process
cfg, err := config.LoadOrDefault()
if err != nil {
return err
}
isDefault, err := addKeyCore(cfg, name, keyPath, certPath, ctx.Bool(keyDefaultFlag.Name))

isDefault := ctx.Bool(keyDefaultFlag.Name)
err = addKeyCore(cfg, key, isDefault)
if err != nil {
return err
}

if err := cfg.Save(); err != nil {
return err
}

// write out
if isDefault {
fmt.Printf("%s: marked as default\n", name)
fmt.Printf("%s: marked as default\n", key.Name)
} else {
fmt.Println(name)
fmt.Println(key.Name)
}
return nil
}

func addKeyCore(cfg *config.File, name, keyPath, certPath string, markDefault bool) (bool, error) {
if ok := cfg.SigningKeys.Keys.Append(name, keyPath, certPath); !ok {
return false, errors.New(name + ": already exists")
func addExternalKey(ctx *cli.Context, pluginName, keyName string) (config.KeySuite, error) {
id := ctx.String("id")
if id == "" {
return config.KeySuite{}, errors.New("missing key id")
}
mgr := manager.NewManager()
p, err := mgr.Get(ctx.Context, pluginName)
if err != nil {
return config.KeySuite{}, err
}
if p.Err != nil {
return config.KeySuite{}, fmt.Errorf("invalid plugin: %w", p.Err)
}
return config.KeySuite{
Name: keyName,
ExternalKey: &config.ExternalKey{ID: id, PluginName: pluginName},
}, nil
}

func newX509KeyPair(ctx *cli.Context, keyName string) (config.KeySuite, error) {
args := ctx.Args()
switch args.Len() {
case 0:
return config.KeySuite{}, errors.New("missing key and certificate paths")
case 1:
return config.KeySuite{}, errors.New("missing certificate path for the corresponding key")
}

keyPath, err := filepath.Abs(args.Get(0))
if err != nil {
return config.KeySuite{}, err
}
certPath, err := filepath.Abs(args.Get(1))
if err != nil {
return config.KeySuite{}, err
}

// check key / cert pair
if _, err := tls.LoadX509KeyPair(certPath, keyPath); err != nil {
return config.KeySuite{}, err
}
return config.KeySuite{
Name: keyName,
X509KeyPair: &config.X509KeyPair{KeyPath: keyPath, CertificatePath: certPath},
}, nil
}

func addKeyCore(cfg *config.File, key config.KeySuite, markDefault bool) error {
if slices.Contains(cfg.SigningKeys.Keys, key.Name) {
return errors.New(key.Name + ": already exists")
}
cfg.SigningKeys.Keys = append(cfg.SigningKeys.Keys, key)
if markDefault {
cfg.SigningKeys.Default = name
cfg.SigningKeys.Default = key.Name
}
return cfg.SigningKeys.Default == name, nil
return nil
}

func updateKey(ctx *cli.Context) error {
Expand All @@ -142,7 +190,7 @@ func updateKey(ctx *cli.Context) error {
if err != nil {
return err
}
if _, _, ok := cfg.SigningKeys.Keys.Get(name); !ok {
if !slices.Contains(cfg.SigningKeys.Keys, name) {
return errors.New(name + ": not found")
}
if !ctx.Bool(keyDefaultFlag.Name) {
Expand All @@ -168,8 +216,7 @@ func listKeys(ctx *cli.Context) error {
}

// write out
printKeySet(cfg.SigningKeys.Default, cfg.SigningKeys.Keys)
return nil
return ioutil.PrintKeyMap(os.Stdout, cfg.SigningKeys.Default, cfg.SigningKeys.Keys)
}

func removeKeys(ctx *cli.Context) error {
Expand All @@ -188,9 +235,11 @@ func removeKeys(ctx *cli.Context) error {
prevDefault := cfg.SigningKeys.Default
var removedNames []string
for _, name := range names {
if ok := cfg.SigningKeys.Keys.Remove(name); !ok {
idx := slices.Index(cfg.SigningKeys.Keys, name)
if idx < 0 {
return errors.New(name + ": not found")
}
cfg.SigningKeys.Keys = slices.Delete(cfg.SigningKeys.Keys, idx)
removedNames = append(removedNames, name)
if prevDefault == name {
cfg.SigningKeys.Default = ""
Expand All @@ -210,29 +259,3 @@ func removeKeys(ctx *cli.Context) error {
}
return nil
}

func printKeySet(target string, s config.KeyMap) {
if len(s) == 0 {
fmt.Println("NAME\tPATH")
return
}

var maxNameSize, maxKeyPathSize int
for _, ref := range s {
if len(ref.Name) > maxNameSize {
maxNameSize = len(ref.Name)
}
if len(ref.KeyPath) > maxKeyPathSize {
maxKeyPathSize = len(ref.KeyPath)
}
}
format := fmt.Sprintf("%%c %%-%ds\t%%-%ds\t%%s\n", maxNameSize, maxKeyPathSize)
fmt.Printf(format, ' ', "NAME", "KEY PATH", "CERTIFICATE PATH")
for _, ref := range s {
mark := ' '
if ref.Name == target {
mark = '*'
}
fmt.Printf(format, mark, ref.Name, ref.KeyPath, ref.CertificatePath)
}
}
1 change: 1 addition & 0 deletions cmd/notation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func main() {
certCommand,
keyCommand,
cacheCommand,
pluginCommand,
},
}
if err := app.Run(os.Args); err != nil {
Expand Down
35 changes: 35 additions & 0 deletions cmd/notation/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"os"

"github.com/notaryproject/notation-go/plugin/manager"
"github.com/notaryproject/notation/internal/ioutil"
"github.com/urfave/cli/v2"
)

var (
pluginCommand = &cli.Command{
Name: "plugin",
Usage: "Manage plugins",
Subcommands: []*cli.Command{
pluginListCommand,
},
}

pluginListCommand = &cli.Command{
Name: "list",
Usage: "List registered plugins",
Aliases: []string{"ls"},
Action: listPlugins,
}
)

func listPlugins(ctx *cli.Context) error {
mgr := manager.NewManager()
shizhMSFT marked this conversation as resolved.
Show resolved Hide resolved
plugins, err := mgr.List(ctx.Context)
if err != nil {
return err
}
return ioutil.PrintPlugins(os.Stdout, plugins)
}
Loading