Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cmd/notation/cert_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@ func generateTestCert(ctx *cli.Context) error {
return err
}
isDefault := ctx.Bool(keyDefaultFlag.Name)
keySuite := config.KeySuite{Name: name, X509KeyPair: &config.X509KeyPair{KeyPath: keyPath, CertificatePath: certPath}}
keySuite := config.KeySuite{
Name: name,
X509KeyPair: &config.X509KeyPair{
KeyPath: keyPath,
CertificatePath: certPath,
},
}
err = addKeyCore(cfg, keySuite, ctx.Bool(keyDefaultFlag.Name))
if err != nil {
return err
Expand Down
16 changes: 13 additions & 3 deletions cmd/notation/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path/filepath"

"github.com/notaryproject/notation-go/plugin/manager"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/internal/ioutil"
"github.com/notaryproject/notation/internal/slices"
"github.com/notaryproject/notation/pkg/config"
Expand Down Expand Up @@ -52,6 +53,7 @@ var (
Name: "id",
Usage: "key id (required if --plugin is set)",
},
cmd.FlagPluginConfig,
keyDefaultFlag,
},
Action: addKey,
Expand Down Expand Up @@ -125,17 +127,25 @@ func addExternalKey(ctx *cli.Context, pluginName, keyName string) (config.KeySui
if id == "" {
return config.KeySuite{}, errors.New("missing key id")
}
mgr := manager.NewManager()
mgr := manager.New(config.PluginDirPath)
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)
}
pluginConfig, err := cmd.ParseFlagPluginConfig(ctx.StringSlice(cmd.FlagPluginConfig.Name))
if err != nil {
return config.KeySuite{}, err
}
return config.KeySuite{
Name: keyName,
ExternalKey: &config.ExternalKey{ID: id, PluginName: pluginName},
Name: keyName,
ExternalKey: &config.ExternalKey{
ID: id,
PluginName: pluginName,
PluginConfig: pluginConfig,
},
}, nil
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/notation/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

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

Expand All @@ -26,7 +27,7 @@ var (
)

func listPlugins(ctx *cli.Context) error {
mgr := manager.NewManager()
mgr := manager.New(config.PluginDirPath)
plugins, err := mgr.List(ctx.Context)
if err != nil {
return err
Expand Down
16 changes: 15 additions & 1 deletion cmd/notation/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/crypto/timestamp"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/internal/osutil"
"github.com/notaryproject/notation/pkg/config"
Expand Down Expand Up @@ -37,6 +38,7 @@ var signCommand = &cli.Command{
flagPassword,
flagPlainHTTP,
flagMediaType,
cmd.FlagPluginConfig,
},
Action: runSign,
}
Expand Down Expand Up @@ -94,7 +96,19 @@ func prepareSigningContent(ctx *cli.Context) (notation.Descriptor, notation.Sign
"identity": identity,
}
}
var tsa timestamp.Timestamper
if endpoint := ctx.String(cmd.FlagTimestamp.Name); endpoint != "" {
if tsa, err = timestamp.NewHTTPTimestamper(nil, endpoint); err != nil {
return notation.Descriptor{}, notation.SignOptions{}, err
}
}
pluginConfig, err := cmd.ParseFlagPluginConfig(ctx.StringSlice(cmd.FlagPluginConfig.Name))
if err != nil {
return notation.Descriptor{}, notation.SignOptions{}, err
}
return manifestDesc, notation.SignOptions{
Expiry: cmd.GetExpiry(ctx),
Expiry: cmd.GetExpiry(ctx),
TSA: tsa,
PluginConfig: pluginConfig,
}, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.18
require (
github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3
github.com/docker/cli v20.10.14+incompatible
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220504164459-182873bded16
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220518191708-407537596ed5
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2
github.com/oras-project/artifacts-spec v1.0.0-draft.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220504164459-182873bded16 h1:pcT6WLHGv1iZ7Z/kflT2NJbuNIqLxuDj2qSfjxE5N3M=
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220504164459-182873bded16/go.mod h1:KtNtijh22iUsC3y7KTzllwPoDKV7mJANYz/RunvOhqs=
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220518191708-407537596ed5 h1:gOayVV8HsSFN4BYLizWAMBtjoxSn944tjIxvwDgYmAY=
github.com/notaryproject/notation-go v0.8.0-alpha.1.0.20220518191708-407537596ed5/go.mod h1:KtNtijh22iUsC3y7KTzllwPoDKV7mJANYz/RunvOhqs=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
Expand Down
31 changes: 30 additions & 1 deletion internal/cmd/flags.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Package cmd contains common flags and routines for all CLIs.
package cmd

import "github.com/urfave/cli/v2"
import (
"fmt"
"strings"

"github.com/urfave/cli/v2"
)

var (
FlagKey = &cli.StringFlag{
Expand Down Expand Up @@ -39,4 +44,28 @@ var (
Aliases: []string{"r"},
Usage: "original reference",
}

FlagPluginConfig = &cli.StringSliceFlag{
Name: "pluginConfig",
Aliases: []string{"pc"},
Usage: "list of comma-separated {key}={value} pairs that are passed as is to the plugin, refer plugin documentation to set appropriate values",
}
)

func ParseFlagPluginConfig(pluginConfigSlice []string) (map[string]string, error) {
if len(pluginConfigSlice) == 0 {
return nil, nil
}
m := make(map[string]string, len(pluginConfigSlice))
for _, c := range pluginConfigSlice {
if k, v, ok := strings.Cut(c, "="); ok {
if _, exist := m[k]; exist {
return nil, fmt.Errorf("duplicated --pluginConfig entry %s", k)
}
m[k] = v
} else {
return nil, fmt.Errorf("malformed --pluginConfig entry %q", c)
}
}
return m, nil
}
39 changes: 39 additions & 0 deletions internal/cmd/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package cmd contains common flags and routines for all CLIs.
package cmd

import (
"reflect"
"testing"
)

func TestParseFlagPluginConfig(t *testing.T) {
type args struct {
s []string
}
tests := []struct {
name string
args args
want map[string]string
wantErr bool
}{
{"nil", args{nil}, nil, false},
{"empty", args{[]string{}}, nil, false},
{"single", args{[]string{"a=b"}}, map[string]string{"a": "b"}, false},
{"multiple", args{[]string{"a=b", "c=d"}}, map[string]string{"a": "b", "c": "d"}, false},
{"quoted", args{[]string{"a=b", "\"c\"=d"}}, map[string]string{"a": "b", "\"c\"": "d"}, false},
{"duplicated", args{[]string{"a=b", "a=d"}}, nil, true},
{"malformed", args{[]string{"a=b", "c:d"}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseFlagPluginConfig(tt.args.s)
if (err != nil) != tt.wantErr {
t.Errorf("ParseFlagPluginConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseFlagPluginConfig() = %v, want %v", got, tt.want)
}
})
}
}
46 changes: 24 additions & 22 deletions internal/cmd/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,44 @@ import (
"time"

"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/crypto/timestamp"
"github.com/notaryproject/notation-go/plugin/manager"
"github.com/notaryproject/notation-go/signature/jws"
"github.com/notaryproject/notation/pkg/config"
"github.com/notaryproject/notation/pkg/signature"
"github.com/urfave/cli/v2"
)

// GetSigner returns a signer according to the CLI context.
func GetSigner(ctx *cli.Context) (notation.Signer, error) {
// read paths of the signing key and its corresponding cert.
var keyPath, certPath string
if path := ctx.String(FlagKeyFile.Name); path != "" {
keyPath = path
certPath = ctx.String(FlagCertFile.Name)
} else {
key, err := config.ResolveKey(ctx.String(FlagKey.Name))
if err != nil {
return nil, err
}
if key.X509KeyPair == nil {
return nil, errors.New("invalid key type")
}
keyPath = key.KeyPath
certPath = key.CertificatePath
// Construct a signer from key and cert file if provided as CLI arguments
if keyPath := ctx.String(FlagKeyFile.Name); keyPath != "" {
certPath := ctx.String(FlagCertFile.Name)
return signature.NewSignerFromFiles(keyPath, certPath)
}

// construct signer
signer, err := signature.NewSignerFromFiles(keyPath, certPath)
// Construct a signer from preconfigured key pair in config.json
// if key name is provided as the CLI argument
key, err := config.ResolveKey(ctx.String(FlagKey.Name))
if err != nil {
return nil, err
}
if endpoint := ctx.String(FlagTimestamp.Name); endpoint != "" {
if signer.TSA, err = timestamp.NewHTTPTimestamper(nil, endpoint); err != nil {
if key.X509KeyPair != nil {
return signature.NewSignerFromFiles(key.X509KeyPair.KeyPath, key.X509KeyPair.CertificatePath)
}
// Construct a plugin signer if key name provided as the CLI argument
// corresponds to an external key
if key.ExternalKey != nil {
mgr := manager.New(config.PluginDirPath)
runner, err := mgr.Runner(key.PluginName)
if err != nil {
return nil, err
}
return &jws.PluginSigner{
Runner: runner,
KeyID: key.ExternalKey.ID,
PluginConfig: key.PluginConfig,
}, nil
}
return signer, nil
return nil, errors.New("unsupported key, either provide a local key and certificate file paths, or a key name in config.json, check [DOC_PLACEHOLDER] for details")
}

// GetExpiry returns the signature expiry according to the CLI context.
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type X509KeyPair struct {
// ExternalKey contains the necessary information to delegate
// the signing operation to the named plugin.
type ExternalKey struct {
ID string `json:"id,omitempty"`
PluginName string `json:"pluginName,omitempty"`
ID string `json:"id,omitempty"`
PluginName string `json:"pluginName,omitempty"`
PluginConfig map[string]string `json:"pluginConfig,omitempty"`
}

// KeySuite is a named key suite.
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const (

// CertificateExtension defines the extension of the certificate files
CertificateExtension = ".crt"

// PluginStoreDirName is the name of the plugin store directory
PluginStoreDirName = "plugins"
)

var (
Expand All @@ -45,6 +48,9 @@ var (

// CertificateStoreDirPath is the path of the certificate store
CertificateStoreDirPath string

// PluginDirPath is the path of the plugin store
PluginDirPath string
)

// init initialize the essential file paths
Expand All @@ -66,6 +72,7 @@ func init() {
SignatureStoreDirPath = filepath.Join(cacheDir, SignatureStoreDirName)
KeyStoreDirPath = filepath.Join(configDir, KeyStoreDirName)
CertificateStoreDirPath = filepath.Join(configDir, CertificateStoreDirName)
PluginDirPath = filepath.Join(configDir, PluginStoreDirName)
}

// SignatureRootPath returns the root path of signatures for a manifest
Expand Down
3 changes: 3 additions & 0 deletions pkg/signature/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func NewSignerFromFiles(keyPath, certPath string) (*jws.Signer, error) {
if err != nil {
return nil, err
}
if len(cert.Certificate) == 0 {
return nil, fmt.Errorf("%q does not contain a signer certificate chain", certPath)
}

// parse cert
certs := make([]*x509.Certificate, len(cert.Certificate))
Expand Down