From 73fefc3f804aca4d300b9993a7036b422603f495 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 10 Apr 2024 15:01:20 +0200 Subject: [PATCH 01/11] Migrate from Poc Signed-off-by: Kim Christensen --- cmd/porter/bundle.go | 1 + cmd/porter/installations.go | 1 + pkg/config/config.go | 12 + pkg/config/datastore.go | 15 + pkg/porter/helpers.go | 6 +- pkg/porter/install.go | 17 + pkg/porter/internal_plugins.go | 25 ++ pkg/porter/lifecycle.go | 2 + pkg/porter/porter.go | 8 +- pkg/porter/publish.go | 24 ++ pkg/signing/helpers.go | 13 + pkg/signing/plugin_adapter.go | 36 ++ pkg/signing/plugins/cosign/doc.go | 3 + pkg/signing/plugins/cosign/plugin.go | 39 +++ pkg/signing/plugins/cosign/signer.go | 74 ++++ pkg/signing/plugins/mock/doc.go | 3 + pkg/signing/plugins/mock/plugin.go | 23 ++ pkg/signing/plugins/mock/signer.go | 56 +++ pkg/signing/plugins/notation/doc.go | 3 + pkg/signing/plugins/notation/plugin.go | 37 ++ pkg/signing/plugins/notation/signer.go | 70 ++++ pkg/signing/plugins/proto/doc.go | 4 + .../plugins/proto/signing_protocol.pb.go | 323 ++++++++++++++++++ .../plugins/proto/signing_protocol.proto | 21 ++ .../plugins/proto/signing_protocol_grpc.pb.go | 141 ++++++++ pkg/signing/plugins/signing_plugin.go | 19 ++ pkg/signing/plugins/signing_protocol.go | 15 + pkg/signing/pluginstore/doc.go | 3 + pkg/signing/pluginstore/grpc.go | 67 ++++ pkg/signing/pluginstore/plugin.go | 38 +++ pkg/signing/pluginstore/signer.go | 113 ++++++ pkg/signing/signer.go | 14 + 32 files changed, 1222 insertions(+), 4 deletions(-) create mode 100644 pkg/signing/helpers.go create mode 100644 pkg/signing/plugin_adapter.go create mode 100644 pkg/signing/plugins/cosign/doc.go create mode 100644 pkg/signing/plugins/cosign/plugin.go create mode 100644 pkg/signing/plugins/cosign/signer.go create mode 100644 pkg/signing/plugins/mock/doc.go create mode 100644 pkg/signing/plugins/mock/plugin.go create mode 100644 pkg/signing/plugins/mock/signer.go create mode 100644 pkg/signing/plugins/notation/doc.go create mode 100644 pkg/signing/plugins/notation/plugin.go create mode 100644 pkg/signing/plugins/notation/signer.go create mode 100644 pkg/signing/plugins/proto/doc.go create mode 100644 pkg/signing/plugins/proto/signing_protocol.pb.go create mode 100644 pkg/signing/plugins/proto/signing_protocol.proto create mode 100644 pkg/signing/plugins/proto/signing_protocol_grpc.pb.go create mode 100644 pkg/signing/plugins/signing_plugin.go create mode 100644 pkg/signing/plugins/signing_protocol.go create mode 100644 pkg/signing/pluginstore/doc.go create mode 100644 pkg/signing/pluginstore/grpc.go create mode 100644 pkg/signing/pluginstore/plugin.go create mode 100644 pkg/signing/pluginstore/signer.go create mode 100644 pkg/signing/signer.go diff --git a/cmd/porter/bundle.go b/cmd/porter/bundle.go index 237e81b78..8c4a537ea 100644 --- a/cmd/porter/bundle.go +++ b/cmd/porter/bundle.go @@ -177,6 +177,7 @@ Note: if overrides for registry/tag/reference are provided, this command only re "viper-key": {"force-overwrite"}, } f.BoolVar(&opts.AutoBuildDisabled, "autobuild-disabled", false, "Do not automatically build the bundle from source when the last build is out-of-date.") + f.BoolVar(&opts.SignBundle, "sign-bundle", false, "Sign the bundle using the configured signing plugin") return &cmd } diff --git a/cmd/porter/installations.go b/cmd/porter/installations.go index 388384972..66848031f 100644 --- a/cmd/porter/installations.go +++ b/cmd/porter/installations.go @@ -255,6 +255,7 @@ The docker driver runs the bundle container using the local Docker host. To use "Create the installation in the specified namespace. Defaults to the global namespace.") f.StringSliceVarP(&opts.Labels, "label", "l", nil, "Associate the specified labels with the installation. May be specified multiple times.") + f.BoolVar(&opts.VerifyBundleBeforeExecution, "verify-bundle", false, "Verify the bundle signature before executing") addBundleActionFlags(f, opts) // Allow configuring the --driver flag with runtime-driver, to avoid conflicts with other commands diff --git a/pkg/config/config.go b/pkg/config/config.go index 40c5810c6..9502659d0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -186,6 +186,18 @@ func (c *Config) GetSecretsPlugin(name string) (SecretsPlugin, error) { return SecretsPlugin{}, errors.New("secrets %q not defined") } +func (c *Config) GetSigningPlugin(name string) (SigningPlugin, error) { + if c != nil { + for _, cs := range c.Data.SigningPlugin { + if cs.Name == name { + return cs, nil + } + } + } + + return SigningPlugin{}, errors.New("signing %q not defined") +} + // GetHomeDir determines the absolute path to the porter home directory. // Hierarchy of checks: // - PORTER_HOME diff --git a/pkg/config/datastore.go b/pkg/config/datastore.go index 3d8e6cb6a..b4eeb6bdc 100644 --- a/pkg/config/datastore.go +++ b/pkg/config/datastore.go @@ -64,12 +64,21 @@ type Data struct { // DefaultSecrets to use when one is not specified by a flag. DefaultSecrets string `mapstructure:"default-secrets"` + // DefaultSigningPlugin is the plugin to use when no plugin is specified. + DefaultSigningPlugin string `mapstructure:"default-signing-plugin"` + + // DefaultSigning to use when one is not specified by a flag. + DefaultSigning string `mapstructure:"default-signer"` + // Namespace is the default namespace for commands that do not override it with a flag. Namespace string `mapstructure:"namespace"` // SecretsPlugin defined in the configuration file. SecretsPlugin []SecretsPlugin `mapstructure:"secrets"` + // SigningPlugin defined in the configuration file. + SigningPlugin []SigningPlugin `mapstructure:"signers"` + // Logs are settings related to Porter's log files. Logs LogConfig `mapstructure:"logs"` @@ -94,11 +103,17 @@ func DefaultDataStore() Data { RuntimeDriver: RuntimeDriverDocker, DefaultStoragePlugin: "mongodb-docker", DefaultSecretsPlugin: "host", + DefaultSigningPlugin: "mock", Logs: LogConfig{Level: "info"}, Verbosity: DefaultVerbosity, } } +// SigningPlugin is the plugin stanza for signing. +type SigningPlugin struct { + PluginConfig `mapstructure:",squash"` +} + // SecretsPlugin is the plugin stanza for secrets. type SecretsPlugin struct { PluginConfig `mapstructure:",squash"` diff --git a/pkg/porter/helpers.go b/pkg/porter/helpers.go index 922514c78..a7f56f3f9 100644 --- a/pkg/porter/helpers.go +++ b/pkg/porter/helpers.go @@ -22,6 +22,7 @@ import ( "get.porter.sh/porter/pkg/mixin" "get.porter.sh/porter/pkg/plugins" "get.porter.sh/porter/pkg/secrets" + "get.porter.sh/porter/pkg/signing" "get.porter.sh/porter/pkg/storage" "get.porter.sh/porter/pkg/tracing" "get.porter.sh/porter/pkg/yaml" @@ -63,13 +64,14 @@ func NewTestPorter(t *testing.T) *TestPorter { tc := config.NewTestConfig(t) testStore := storage.NewTestStore(tc) testSecrets := secrets.NewTestSecretsProvider() + testSigner := signing.NewTestSigningProvider() testCredentials := storage.NewTestCredentialProviderFor(t, testStore, testSecrets) testParameters := storage.NewTestParameterProviderFor(t, testStore, testSecrets) testCache := cache.NewTestCache(cache.New(tc.Config)) testInstallations := storage.NewTestInstallationProviderFor(t, testStore) testRegistry := cnabtooci.NewTestRegistry() - p := NewFor(tc.Config, testStore, testSecrets) + p := NewFor(tc.Config, testStore, testSecrets, testSigner) p.Config = tc.Config p.Mixins = mixin.NewTestMixinProvider() p.Plugins = plugins.NewTestPluginProvider() @@ -113,7 +115,7 @@ func (p *TestPorter) SetupIntegrationTest() context.Context { t := p.TestConfig.TestContext.T // Undo changes above to make a unit test friendly Porter, so we hit the host - p.Porter = NewFor(p.Config, p.TestStore, p.TestSecrets) + p.Porter = NewFor(p.Config, p.TestStore, p.TestSecrets, p.Signatures) // Run the test in a temp directory ctx, testDir, _ := p.TestConfig.SetupIntegrationTest() diff --git a/pkg/porter/install.go b/pkg/porter/install.go index 819987286..4b4e7714e 100644 --- a/pkg/porter/install.go +++ b/pkg/porter/install.go @@ -88,6 +88,23 @@ func (p *Porter) InstallBundle(ctx context.Context, opts InstallOptions) error { return fmt.Errorf("error saving installation record: %w", err) } + if opts.VerifyBundleBeforeExecution { + ref, ok, err := i.Bundle.GetBundleReference() + if err != nil { + return err + } + log.Infof("verifying signature for %s", ref.String()) + if !ok { + return fmt.Errorf("unable to get reference for bundle") + } + err = p.Signatures.Verify(ctx, ref.String()) + if err != nil { + log.Errorf("unable to verify signature %w", err) + return err + } + log.Infof("signature verified for %s", ref.String()) + } + // Run install using the updated installation record return p.ExecuteAction(ctx, i, opts) } diff --git a/pkg/porter/internal_plugins.go b/pkg/porter/internal_plugins.go index 57f45328e..bf2de98dc 100644 --- a/pkg/porter/internal_plugins.go +++ b/pkg/porter/internal_plugins.go @@ -13,6 +13,10 @@ import ( secretsplugins "get.porter.sh/porter/pkg/secrets/plugins" "get.porter.sh/porter/pkg/secrets/plugins/filesystem" "get.porter.sh/porter/pkg/secrets/plugins/host" + signingplugins "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/plugins/cosign" + "get.porter.sh/porter/pkg/signing/plugins/mock" + "get.porter.sh/porter/pkg/signing/plugins/notation" storageplugins "get.porter.sh/porter/pkg/storage/plugins" "get.porter.sh/porter/pkg/storage/plugins/mongodb" "get.porter.sh/porter/pkg/storage/plugins/mongodb_docker" @@ -143,5 +147,26 @@ func getInternalPlugins() map[string]InternalPlugin { return mongodb_docker.NewPlugin(c.Context, pluginCfg) }, }, + mock.PluginKey: { + Interface: signingplugins.PluginInterface, + ProtocolVersion: signingplugins.PluginProtocolVersion, + Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) { + return mock.NewPlugin(c.Context, pluginCfg) + }, + }, + notation.PluginKey: { + Interface: signingplugins.PluginInterface, + ProtocolVersion: signingplugins.PluginProtocolVersion, + Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) { + return notation.NewPlugin(c.Context, pluginCfg) + }, + }, + cosign.PluginKey: { + Interface: signingplugins.PluginInterface, + ProtocolVersion: signingplugins.PluginProtocolVersion, + Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) { + return cosign.NewPlugin(c.Context, pluginCfg) + }, + }, } } diff --git a/pkg/porter/lifecycle.go b/pkg/porter/lifecycle.go index 6d3e46350..30aacc625 100644 --- a/pkg/porter/lifecycle.go +++ b/pkg/porter/lifecycle.go @@ -77,6 +77,8 @@ type BundleExecutionOptions struct { // A cache of the final resolved set of parameters that are passed to the bundle // Do not use directly, use GetParameters instead. finalParams map[string]interface{} + + VerifyBundleBeforeExecution bool } func NewBundleExecutionOptions() *BundleExecutionOptions { diff --git a/pkg/porter/porter.go b/pkg/porter/porter.go index 20f007ad6..2bd4c4f75 100644 --- a/pkg/porter/porter.go +++ b/pkg/porter/porter.go @@ -17,6 +17,8 @@ import ( "get.porter.sh/porter/pkg/plugins" "get.porter.sh/porter/pkg/secrets" secretsplugin "get.porter.sh/porter/pkg/secrets/pluginstore" + "get.porter.sh/porter/pkg/signing" + signingplugin "get.porter.sh/porter/pkg/signing/pluginstore" "get.porter.sh/porter/pkg/storage" "get.porter.sh/porter/pkg/storage/migrations" storageplugin "get.porter.sh/porter/pkg/storage/pluginstore" @@ -46,6 +48,7 @@ type Porter struct { CNAB cnabprovider.CNABProvider Secrets secrets.Store Storage storage.Provider + Signatures signing.Signer } // New porter client, initialized with useful defaults. @@ -53,10 +56,11 @@ func New() *Porter { c := config.New() storage := storage.NewPluginAdapter(storageplugin.NewStore(c)) secretStorage := secrets.NewPluginAdapter(secretsplugin.NewStore(c)) - return NewFor(c, storage, secretStorage) + signatures := signing.NewPluginAdapter(signingplugin.NewSigner(c)) + return NewFor(c, storage, secretStorage, signatures) } -func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store) *Porter { +func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store, signer signing.Signer) *Porter { cache := cache.New(c) storageManager := migrations.NewManager(c, store) diff --git a/pkg/porter/publish.go b/pkg/porter/publish.go index b93c296c7..0eb33de06 100644 --- a/pkg/porter/publish.go +++ b/pkg/porter/publish.go @@ -31,6 +31,7 @@ type PublishOptions struct { Tag string Registry string ArchiveFile string + SignBundle bool } // Validate performs validation on the publish options @@ -215,6 +216,25 @@ func (p *Porter) publishFromFile(ctx context.Context, opts PublishOptions) error return err } + log.Infof("should I sign the bundle? %s", opts.SignBundle) + if opts.SignBundle { + log.Infof("signing bundle %s", bundleRef.String()) + inImage, err := cnab.CalculateTemporaryImageTag(bundleRef.Reference) + if err != nil { + return err + } + log.Infof("Signing invocation image %s.", inImage.String()) + err = p.Signatures.Sign(context.Background(), inImage.String()) + if err != nil { + return err + } + log.Infof("Signing bundle artifact %s.", bundleRef.Reference.String()) + p.Signatures.Sign(context.Background(), bundleRef.Reference.String()) + if err != nil { + return err + } + } + // Perhaps we have a cached version of a bundle with the same reference, previously pulled // If so, replace it, as it is most likely out-of-date per this publish err = p.refreshCachedBundle(bundleRef) @@ -471,3 +491,7 @@ func (p *Porter) refreshCachedBundle(bundleRef cnab.BundleReference) error { } return nil } + +func (p *Porter) signBundle(bundleRef cnab.BundleReference) error { + return nil +} diff --git a/pkg/signing/helpers.go b/pkg/signing/helpers.go new file mode 100644 index 000000000..e8e789c15 --- /dev/null +++ b/pkg/signing/helpers.go @@ -0,0 +1,13 @@ +package signing + +var _ Signer = &TestSigningProvider{} + +type TestSigningProvider struct { + PluginAdapter + //TODO: add a test signer here +} + +func NewTestSigningProvider() TestSigningProvider { + // TODO: implement this + return TestSigningProvider{} +} diff --git a/pkg/signing/plugin_adapter.go b/pkg/signing/plugin_adapter.go new file mode 100644 index 000000000..cef3f4241 --- /dev/null +++ b/pkg/signing/plugin_adapter.go @@ -0,0 +1,36 @@ +package signing + +import ( + "context" + "io" + + "get.porter.sh/porter/pkg/signing/plugins" +) + +var _ Signer = PluginAdapter{} + +// PluginAdapter converts between the low-level plugins.SigningProtocol and +// the signing.Signer interface. +type PluginAdapter struct { + plugin plugins.SigningProtocol +} + +// NewPluginAdapter wraps the specified storage plugin. +func NewPluginAdapter(plugin plugins.SigningProtocol) PluginAdapter { + return PluginAdapter{plugin: plugin} +} + +func (a PluginAdapter) Close() error { + if closer, ok := a.plugin.(io.Closer); ok { + return closer.Close() + } + return nil +} + +func (a PluginAdapter) Sign(ctx context.Context, ref string) error { + return a.plugin.Sign(ctx, ref) +} + +func (a PluginAdapter) Verify(ctx context.Context, ref string) error { + return a.plugin.Verify(ctx, ref) +} diff --git a/pkg/signing/plugins/cosign/doc.go b/pkg/signing/plugins/cosign/doc.go new file mode 100644 index 000000000..8c13a50f3 --- /dev/null +++ b/pkg/signing/plugins/cosign/doc.go @@ -0,0 +1,3 @@ +// Package mock provides an in-memory implementation of a secret store +// suitable for unit testing. +package cosign diff --git a/pkg/signing/plugins/cosign/plugin.go b/pkg/signing/plugins/cosign/plugin.go new file mode 100644 index 000000000..cd3ef687f --- /dev/null +++ b/pkg/signing/plugins/cosign/plugin.go @@ -0,0 +1,39 @@ +package cosign + +import ( + "fmt" + + "get.porter.sh/porter/pkg/config" + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/pluginstore" + "github.com/hashicorp/go-plugin" + "github.com/mitchellh/mapstructure" +) + +const PluginKey = plugins.PluginInterface + ".porter.cosign" + +var _ plugins.SigningProtocol = &Plugin{} + +type PluginConfig struct { + //theses are paths + PublicKey string `mapstructure:"publickey,omitempty"` + PrivateKey string `mapstructure:"privatekey,omitempty"` +} + +// Plugin is the plugin wrapper for accessing secrets from a local filesystem. +type Plugin struct { + config *config.Config + signing.Signer +} + +func NewPlugin(c *portercontext.Context, rawCfg interface{}) (plugin.Plugin, error) { + cfg := PluginConfig{} + if err := mapstructure.Decode(rawCfg, &cfg); err != nil { + return nil, fmt.Errorf("error reading plugin configuration: %w", err) + } + + impl := NewSigner(c, cfg) + return pluginstore.NewPlugin(c, impl), nil +} diff --git a/pkg/signing/plugins/cosign/signer.go b/pkg/signing/plugins/cosign/signer.go new file mode 100644 index 000000000..be5adbdca --- /dev/null +++ b/pkg/signing/plugins/cosign/signer.go @@ -0,0 +1,74 @@ +package cosign + +import ( + "context" + "fmt" + "os/exec" + + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/tracing" +) + +var _ plugins.SigningProtocol = &Cosign{} + +// Signer implements an in-memory signer for testing. +type Cosign struct { + PublicKey string + PrivateKey string +} + +func NewSigner(c *portercontext.Context, cfg PluginConfig) *Cosign { + + s := &Cosign{ + PublicKey: cfg.PublicKey, + PrivateKey: cfg.PrivateKey, + } + + return s +} + +// we should get the certificate... here? +func (s *Cosign) Connect(ctx context.Context) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Debug("Running cosign signer") + + return nil +} + +// Close implements the Close method on the signing plugins' interface. +func (s *Cosign) Close() error { + return nil +} + +func (s *Cosign) Sign(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + log.Infof("Cosign Signer is Signing %s", ref) + cmd := exec.Command("cosign", "sign", ref, "--tlog-upload=false", "--key", s.PrivateKey) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %w", string(out), err) + } + log.Infof("%s", out) + return nil +} + +func (s *Cosign) Verify(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Infof("Mock Signer is Verifying %s", ref) + cmd := exec.Command("cosign", "verify", "--key", s.PublicKey, ref, "--insecure-ignore-tlog") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %w", string(out), err) + } + log.Infof("%s", out) + return nil +} diff --git a/pkg/signing/plugins/mock/doc.go b/pkg/signing/plugins/mock/doc.go new file mode 100644 index 000000000..0c680d461 --- /dev/null +++ b/pkg/signing/plugins/mock/doc.go @@ -0,0 +1,3 @@ +// Package mock provides an in-memory implementation of a secret store +// suitable for unit testing. +package mock diff --git a/pkg/signing/plugins/mock/plugin.go b/pkg/signing/plugins/mock/plugin.go new file mode 100644 index 000000000..5f432e69c --- /dev/null +++ b/pkg/signing/plugins/mock/plugin.go @@ -0,0 +1,23 @@ +package mock + +import ( + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/pluginstore" + "github.com/hashicorp/go-plugin" +) + +const PluginKey = plugins.PluginInterface + ".porter.mock" + +var _ plugins.SigningProtocol = &Plugin{} + +// Plugin is the plugin wrapper for accessing secrets from a local filesystem. +type Plugin struct { + signing.Signer +} + +func NewPlugin(c *portercontext.Context, rawCfg interface{}) (plugin.Plugin, error) { + impl := NewSigner() + return pluginstore.NewPlugin(c, impl), nil +} diff --git a/pkg/signing/plugins/mock/signer.go b/pkg/signing/plugins/mock/signer.go new file mode 100644 index 000000000..6e65bf159 --- /dev/null +++ b/pkg/signing/plugins/mock/signer.go @@ -0,0 +1,56 @@ +package mock + +import ( + "context" + b64 "encoding/base64" + + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/tracing" +) + +var _ plugins.SigningProtocol = &Signer{} + +// Signer implements an in-memory signer for testing. +type Signer struct { + Signatures map[string]string +} + +func NewSigner() *Signer { + s := &Signer{ + Signatures: make(map[string]string), + } + + return s +} + +func (s *Signer) Connect(ctx context.Context) error { + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Debug("Running mock signer") + + return nil +} + +// Close implements the Close method on the signing plugins' interface. +func (s *Signer) Close() error { + return nil +} + +func (s *Signer) Sign(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Infof("Mock Signer is Signing %s", ref) + s.Signatures[ref] = b64.StdEncoding.EncodeToString([]byte(ref)) + return nil +} + +func (s *Signer) Verify(ctx context.Context, ref string) error { + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Infof("Mock Signer is Verifying %s", ref) + return nil +} diff --git a/pkg/signing/plugins/notation/doc.go b/pkg/signing/plugins/notation/doc.go new file mode 100644 index 000000000..289752dc0 --- /dev/null +++ b/pkg/signing/plugins/notation/doc.go @@ -0,0 +1,3 @@ +// Package notation provides an implementation of a signing plugin +// using the users's local notation installation +package notation diff --git a/pkg/signing/plugins/notation/plugin.go b/pkg/signing/plugins/notation/plugin.go new file mode 100644 index 000000000..21d69ca82 --- /dev/null +++ b/pkg/signing/plugins/notation/plugin.go @@ -0,0 +1,37 @@ +package notation + +import ( + "fmt" + + "get.porter.sh/porter/pkg/config" + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/pluginstore" + "github.com/hashicorp/go-plugin" + "github.com/mitchellh/mapstructure" +) + +const PluginKey = plugins.PluginInterface + ".porter.notation" + +var _ plugins.SigningProtocol = &Plugin{} + +type PluginConfig struct { + SigningKey string `mapstructure:"key,omitempty"` +} + +// Plugin is the plugin wrapper for accessing secrets from a local filesystem. +type Plugin struct { + config *config.Config + signing.Signer +} + +func NewPlugin(c *portercontext.Context, rawCfg interface{}) (plugin.Plugin, error) { + cfg := PluginConfig{} + if err := mapstructure.Decode(rawCfg, &cfg); err != nil { + return nil, fmt.Errorf("error reading plugin configuration: %w", err) + } + + impl := NewSigner(c, cfg) + return pluginstore.NewPlugin(c, impl), nil +} diff --git a/pkg/signing/plugins/notation/signer.go b/pkg/signing/plugins/notation/signer.go new file mode 100644 index 000000000..23f86e1a4 --- /dev/null +++ b/pkg/signing/plugins/notation/signer.go @@ -0,0 +1,70 @@ +package notation + +import ( + "context" + "fmt" + "os/exec" + + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/tracing" +) + +var _ plugins.SigningProtocol = &Signer{} + +// Signer implements an in-memory signer for testing. +type Signer struct { + + // Need the key we want to use + SigningKey string +} + +func NewSigner(c *portercontext.Context, cfg PluginConfig) *Signer { + s := &Signer{ + SigningKey: cfg.SigningKey, + } + return s +} + +func (s *Signer) Connect(ctx context.Context) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + log.Debug("Running mock signer") + + return nil +} + +// Close implements the Close method on the signing plugins' interface. +func (s *Signer) Close() error { + return nil +} + +func (s *Signer) Sign(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + cmd := exec.Command("notation", "sign", ref, "--key", s.SigningKey) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %w", string(out), err) + } + log.Infof("%s", out) + return nil +} + +func (s *Signer) Verify(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now + ctx, log := tracing.StartSpan(ctx) + defer log.EndSpan() + + cmd := exec.Command("notation", "verify", ref) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %w", string(out), err) + } + log.Infof("%s", out) + return nil +} diff --git a/pkg/signing/plugins/proto/doc.go b/pkg/signing/plugins/proto/doc.go new file mode 100644 index 000000000..2062163e9 --- /dev/null +++ b/pkg/signing/plugins/proto/doc.go @@ -0,0 +1,4 @@ +//go:generate protoc pkg/signing/plugins/proto/signing_protocol.proto --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative --proto_path=. + +// Package proto is the protobuf definition for the SigningProtocol +package proto diff --git a/pkg/signing/plugins/proto/signing_protocol.pb.go b/pkg/signing/plugins/proto/signing_protocol.pb.go new file mode 100644 index 000000000..f9c0c1775 --- /dev/null +++ b/pkg/signing/plugins/proto/signing_protocol.pb.go @@ -0,0 +1,323 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.12.4 +// source: signing_protocol.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SignRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` +} + +func (x *SignRequest) Reset() { + *x = SignRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignRequest) ProtoMessage() {} + +func (x *SignRequest) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignRequest.ProtoReflect.Descriptor instead. +func (*SignRequest) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{0} +} + +func (x *SignRequest) GetRef() string { + if x != nil { + return x.Ref + } + return "" +} + +type VerifyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` +} + +func (x *VerifyRequest) Reset() { + *x = VerifyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VerifyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyRequest) ProtoMessage() {} + +func (x *VerifyRequest) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VerifyRequest.ProtoReflect.Descriptor instead. +func (*VerifyRequest) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{1} +} + +func (x *VerifyRequest) GetRef() string { + if x != nil { + return x.Ref + } + return "" +} + +type SignResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SignResponse) Reset() { + *x = SignResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignResponse) ProtoMessage() {} + +func (x *SignResponse) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignResponse.ProtoReflect.Descriptor instead. +func (*SignResponse) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{2} +} + +type VerifyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *VerifyResponse) Reset() { + *x = VerifyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VerifyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyResponse) ProtoMessage() {} + +func (x *VerifyResponse) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VerifyResponse.ProtoReflect.Descriptor instead. +func (*VerifyResponse) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{3} +} + +var File_signing_protocol_proto protoreflect.FileDescriptor + +var file_signing_protocol_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x73, 0x22, 0x1f, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x52, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, + 0x65, 0x66, 0x22, 0x21, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x52, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x52, 0x65, 0x66, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x81, 0x01, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x33, 0x0a, 0x04, 0x53, + 0x69, 0x67, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x39, 0x0a, 0x06, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, + 0x65, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2e, 0x73, 0x68, 0x2f, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2f, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_signing_protocol_proto_rawDescOnce sync.Once + file_signing_protocol_proto_rawDescData = file_signing_protocol_proto_rawDesc +) + +func file_signing_protocol_proto_rawDescGZIP() []byte { + file_signing_protocol_proto_rawDescOnce.Do(func() { + file_signing_protocol_proto_rawDescData = protoimpl.X.CompressGZIP(file_signing_protocol_proto_rawDescData) + }) + return file_signing_protocol_proto_rawDescData +} + +var file_signing_protocol_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_signing_protocol_proto_goTypes = []interface{}{ + (*SignRequest)(nil), // 0: plugins.SignRequest + (*VerifyRequest)(nil), // 1: plugins.VerifyRequest + (*SignResponse)(nil), // 2: plugins.SignResponse + (*VerifyResponse)(nil), // 3: plugins.VerifyResponse +} +var file_signing_protocol_proto_depIdxs = []int32{ + 0, // 0: plugins.SigningProtocol.Sign:input_type -> plugins.SignRequest + 1, // 1: plugins.SigningProtocol.Verify:input_type -> plugins.VerifyRequest + 2, // 2: plugins.SigningProtocol.Sign:output_type -> plugins.SignResponse + 3, // 3: plugins.SigningProtocol.Verify:output_type -> plugins.VerifyResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_signing_protocol_proto_init() } +func file_signing_protocol_proto_init() { + if File_signing_protocol_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_signing_protocol_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_signing_protocol_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VerifyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_signing_protocol_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_signing_protocol_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VerifyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_signing_protocol_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_signing_protocol_proto_goTypes, + DependencyIndexes: file_signing_protocol_proto_depIdxs, + MessageInfos: file_signing_protocol_proto_msgTypes, + }.Build() + File_signing_protocol_proto = out.File + file_signing_protocol_proto_rawDesc = nil + file_signing_protocol_proto_goTypes = nil + file_signing_protocol_proto_depIdxs = nil +} diff --git a/pkg/signing/plugins/proto/signing_protocol.proto b/pkg/signing/plugins/proto/signing_protocol.proto new file mode 100644 index 000000000..bd6412e5d --- /dev/null +++ b/pkg/signing/plugins/proto/signing_protocol.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package plugins; + +option go_package = "get.porter.sh/porter/pkg/signing/plugins/proto"; + +message SignRequest { + string Ref = 1; +} + +message VerifyRequest { + string Ref = 1; +} + +message SignResponse {} + +message VerifyResponse {} + +service SigningProtocol { + rpc Sign(SignRequest) returns (SignResponse); + rpc Verify(VerifyRequest) returns (VerifyResponse); +} \ No newline at end of file diff --git a/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go b/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go new file mode 100644 index 000000000..974540951 --- /dev/null +++ b/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go @@ -0,0 +1,141 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.12.4 +// source: signing_protocol.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// SigningProtocolClient is the client API for SigningProtocol service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SigningProtocolClient interface { + Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*SignResponse, error) + Verify(ctx context.Context, in *VerifyRequest, opts ...grpc.CallOption) (*VerifyResponse, error) +} + +type signingProtocolClient struct { + cc grpc.ClientConnInterface +} + +func NewSigningProtocolClient(cc grpc.ClientConnInterface) SigningProtocolClient { + return &signingProtocolClient{cc} +} + +func (c *signingProtocolClient) Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*SignResponse, error) { + out := new(SignResponse) + err := c.cc.Invoke(ctx, "/plugins.SigningProtocol/Sign", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *signingProtocolClient) Verify(ctx context.Context, in *VerifyRequest, opts ...grpc.CallOption) (*VerifyResponse, error) { + out := new(VerifyResponse) + err := c.cc.Invoke(ctx, "/plugins.SigningProtocol/Verify", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SigningProtocolServer is the server API for SigningProtocol service. +// All implementations must embed UnimplementedSigningProtocolServer +// for forward compatibility +type SigningProtocolServer interface { + Sign(context.Context, *SignRequest) (*SignResponse, error) + Verify(context.Context, *VerifyRequest) (*VerifyResponse, error) + mustEmbedUnimplementedSigningProtocolServer() +} + +// UnimplementedSigningProtocolServer must be embedded to have forward compatible implementations. +type UnimplementedSigningProtocolServer struct { +} + +func (UnimplementedSigningProtocolServer) Sign(context.Context, *SignRequest) (*SignResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented") +} +func (UnimplementedSigningProtocolServer) Verify(context.Context, *VerifyRequest) (*VerifyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Verify not implemented") +} +func (UnimplementedSigningProtocolServer) mustEmbedUnimplementedSigningProtocolServer() {} + +// UnsafeSigningProtocolServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SigningProtocolServer will +// result in compilation errors. +type UnsafeSigningProtocolServer interface { + mustEmbedUnimplementedSigningProtocolServer() +} + +func RegisterSigningProtocolServer(s grpc.ServiceRegistrar, srv SigningProtocolServer) { + s.RegisterService(&SigningProtocol_ServiceDesc, srv) +} + +func _SigningProtocol_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SigningProtocolServer).Sign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugins.SigningProtocol/Sign", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SigningProtocolServer).Sign(ctx, req.(*SignRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SigningProtocol_Verify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VerifyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SigningProtocolServer).Verify(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugins.SigningProtocol/Verify", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SigningProtocolServer).Verify(ctx, req.(*VerifyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// SigningProtocol_ServiceDesc is the grpc.ServiceDesc for SigningProtocol service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SigningProtocol_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "plugins.SigningProtocol", + HandlerType: (*SigningProtocolServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Sign", + Handler: _SigningProtocol_Sign_Handler, + }, + { + MethodName: "Verify", + Handler: _SigningProtocol_Verify_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "signing_protocol.proto", +} diff --git a/pkg/signing/plugins/signing_plugin.go b/pkg/signing/plugins/signing_plugin.go new file mode 100644 index 000000000..faaa18b4d --- /dev/null +++ b/pkg/signing/plugins/signing_plugin.go @@ -0,0 +1,19 @@ +package plugins + +import "errors" + +const ( + // PluginInterface for signing. This first part of the + // three-part plugin key is only seen/used by the plugins when the host is + // communicating with the plugin and is not exposed to users. + PluginInterface = "signing" + + // PluginProtocolVersion is the currently supported plugin protocol version for secrets. + PluginProtocolVersion = 1 +) + +var ( + // ErrNotImplemented is the error to be returned if a method is not implemented + // in a secret plugin + ErrNotImplemented = errors.New("not implemented") +) diff --git a/pkg/signing/plugins/signing_protocol.go b/pkg/signing/plugins/signing_protocol.go new file mode 100644 index 000000000..e6aeb335e --- /dev/null +++ b/pkg/signing/plugins/signing_protocol.go @@ -0,0 +1,15 @@ +package plugins + +import "context" + +// SigningProtocol is the interface that signing plugins must implement. +// This defines the protocol used to communicate with signing plugins. +type SigningProtocol interface { + // Resolve a secret value from a secret store + // - ref is OCI reference to verify + Sign(ctx context.Context, ref string) error + + // Verify attempts to verify the signature of a given reference + // - ref is OCI reference to verify + Verify(ctx context.Context, ref string) error +} diff --git a/pkg/signing/pluginstore/doc.go b/pkg/signing/pluginstore/doc.go new file mode 100644 index 000000000..c129a9004 --- /dev/null +++ b/pkg/signing/pluginstore/doc.go @@ -0,0 +1,3 @@ +// Package pluginstore is an internal Porter package that implements the +// plugins.SigningPlugin interface. +package pluginstore diff --git a/pkg/signing/pluginstore/grpc.go b/pkg/signing/pluginstore/grpc.go new file mode 100644 index 000000000..af2dc7f02 --- /dev/null +++ b/pkg/signing/pluginstore/grpc.go @@ -0,0 +1,67 @@ +package pluginstore + +import ( + "context" + + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/plugins/proto" +) + +var _ plugins.SigningProtocol = &GClient{} + +// GClient is a gRPC implementation of the signing client. +type GClient struct { + client proto.SigningProtocolClient +} + +func NewClient(client proto.SigningProtocolClient) *GClient { + return &GClient{client} +} + +func (m *GClient) Sign(ctx context.Context, ref string) error { + req := &proto.SignRequest{ + Ref: ref, + } + + _, err := m.client.Sign(ctx, req) + if err != nil { + return err + } + return nil +} + +func (m *GClient) Verify(ctx context.Context, ref string) error { + req := &proto.VerifyRequest{ + Ref: ref, + } + _, err := m.client.Verify(ctx, req) + return err +} + +// GServer is a gRPC wrapper around a SecretsProtocol plugin +type GServer struct { + c *portercontext.Context + impl plugins.SigningProtocol + proto.UnsafeSigningProtocolServer +} + +func NewServer(c *portercontext.Context, impl plugins.SigningProtocol) *GServer { + return &GServer{c: c, impl: impl} +} + +func (m *GServer) Sign(ctx context.Context, request *proto.SignRequest) (*proto.SignResponse, error) { + err := m.impl.Sign(ctx, request.Ref) + if err != nil { + return nil, err + } + return &proto.SignResponse{}, nil +} + +func (m *GServer) Verify(ctx context.Context, request *proto.VerifyRequest) (*proto.VerifyResponse, error) { + err := m.impl.Verify(ctx, request.Ref) + if err != nil { + return nil, err + } + return &proto.VerifyResponse{}, nil +} diff --git a/pkg/signing/pluginstore/plugin.go b/pkg/signing/pluginstore/plugin.go new file mode 100644 index 000000000..b7026fea5 --- /dev/null +++ b/pkg/signing/pluginstore/plugin.go @@ -0,0 +1,38 @@ +package pluginstore + +import ( + "context" + + "get.porter.sh/porter/pkg/portercontext" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/signing/plugins/proto" + "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" +) + +var _ plugin.GRPCPlugin = Plugin{} + +// Plugin is the shared implementation of a storage plugin wrapper. +type Plugin struct { + plugin.Plugin + impl plugins.SigningProtocol + context *portercontext.Context +} + +// NewPlugin creates an instance of a storage plugin. +func NewPlugin(c *portercontext.Context, impl plugins.SigningProtocol) Plugin { + return Plugin{ + context: c, + impl: impl, + } +} + +func (p Plugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + impl := NewServer(p.context, p.impl) + proto.RegisterSigningProtocolServer(s, impl) + return nil +} + +func (p Plugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, conn *grpc.ClientConn) (interface{}, error) { + return NewClient(proto.NewSigningProtocolClient(conn)), nil +} diff --git a/pkg/signing/pluginstore/signer.go b/pkg/signing/pluginstore/signer.go new file mode 100644 index 000000000..d2ed83482 --- /dev/null +++ b/pkg/signing/pluginstore/signer.go @@ -0,0 +1,113 @@ +package pluginstore + +import ( + "context" + "errors" + "fmt" + + "get.porter.sh/porter/pkg/config" + "get.porter.sh/porter/pkg/plugins/pluggable" + "get.porter.sh/porter/pkg/signing/plugins" + "get.porter.sh/porter/pkg/tracing" + "go.opentelemetry.io/otel/attribute" +) + +var _ plugins.SigningProtocol = &Signer{} + +// Signer is a plugin-backed source of signing. It resolves the appropriate +// plugin based on Porter's config and implements the plugins.SigningProtocol interface +// using the backing plugin. +// +// Connects just-in-time, but you must call Close to release resources. +type Signer struct { + *config.Config + plugin plugins.SigningProtocol + conn *pluggable.PluginConnection +} + +func NewSigner(c *config.Config) *Signer { + return &Signer{ + Config: c, + } +} + +// NewSigningPluginConfig for signing sources. +func NewSigningPluginConfig() pluggable.PluginTypeConfig { + return pluggable.PluginTypeConfig{ + Interface: plugins.PluginInterface, + Plugin: &Plugin{}, + GetDefaultPluggable: func(c *config.Config) string { + return c.Data.DefaultSigning + }, + GetPluggable: func(c *config.Config, name string) (pluggable.Entry, error) { + return c.GetSigningPlugin(name) + }, + GetDefaultPlugin: func(c *config.Config) string { + return c.Data.DefaultSigningPlugin + }, + ProtocolVersion: plugins.PluginProtocolVersion, + } +} + +func (s *Signer) Sign(ctx context.Context, ref string) error { + ctx, span := tracing.StartSpan(ctx, + attribute.String("ref", ref)) + defer span.EndSpan() + + if err := s.Connect(ctx); err != nil { + return err + } + + err := s.plugin.Sign(ctx, ref) + if err != nil { + return span.Error(err) + } + return nil +} + +func (s *Signer) Verify(ctx context.Context, ref string) error { + ctx, span := tracing.StartSpan(ctx, + attribute.String("ref", ref)) + defer span.EndSpan() + + if err := s.Connect(ctx); err != nil { + return err + } + + err := s.plugin.Verify(ctx, ref) + if errors.Is(err, plugins.ErrNotImplemented) { + return span.Error(fmt.Errorf(`the current signing plugin does not support verifying signatures. You need to edit your porter configuration file and configure a different signing plugin: %w`, err)) + } + + return span.Error(err) +} + +// Connect initializes the plugin for use. +// The plugin itself is responsible for ensuring it was called. +// Close is called automatically when the plugin is used by Porter. +func (s *Signer) Connect(ctx context.Context) error { + if s.plugin != nil { + return nil + } + + ctx, span := tracing.StartSpan(ctx) + defer span.EndSpan() + + pluginType := NewSigningPluginConfig() + + l := pluggable.NewPluginLoader(s.Config) + conn, err := l.Load(ctx, pluginType) + if err != nil { + return span.Error(err) + } + s.conn = conn + + store, ok := conn.GetClient().(plugins.SigningProtocol) + if !ok { + conn.Close(ctx) + return span.Error(fmt.Errorf("the interface (%T) exposed by the %s plugin was not plugins.SigningProtocol", conn.GetClient(), conn)) + } + s.plugin = store + + return nil +} diff --git a/pkg/signing/signer.go b/pkg/signing/signer.go new file mode 100644 index 000000000..599135d26 --- /dev/null +++ b/pkg/signing/signer.go @@ -0,0 +1,14 @@ +package signing + +import "context" + +// Signer is an interface for signing and verifying Porter +// bundles and invocation images. +type Signer interface { + // Sign generates a signature for the specified reference, which + // can be a Porter bundle or an invocation image. + Sign(ctx context.Context, ref string) error + // Verify attempts to verify a signature for the specified + // reference, which can be a Porter bundle or an invocation image. + Verify(ctx context.Context, ref string) error +} From a38934b1b215410951640abd28604c4746a3848b Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 10 Apr 2024 20:09:26 +0200 Subject: [PATCH 02/11] Cleanup Signed-off-by: Kim Christensen --- pkg/grpc/portergrpc/portergrpc.go | 5 +++- pkg/porter/helpers.go | 2 +- pkg/porter/install.go | 11 ++++---- pkg/porter/porter.go | 8 +++--- pkg/porter/publish.go | 21 ++++++--------- .../plugins/cosign/{signer.go => cosign.go} | 27 ++++++++++++++----- pkg/signing/plugins/cosign/plugin.go | 8 +++--- .../plugins/mock/{signer.go => mock.go} | 1 + .../notation/{signer.go => notation.go} | 2 +- pkg/signing/plugins/notation/plugin.go | 2 -- 10 files changed, 50 insertions(+), 37 deletions(-) rename pkg/signing/plugins/cosign/{signer.go => cosign.go} (67%) rename pkg/signing/plugins/mock/{signer.go => mock.go} (95%) rename pkg/signing/plugins/notation/{signer.go => notation.go} (97%) diff --git a/pkg/grpc/portergrpc/portergrpc.go b/pkg/grpc/portergrpc/portergrpc.go index 53f6201a9..f59e42c4b 100644 --- a/pkg/grpc/portergrpc/portergrpc.go +++ b/pkg/grpc/portergrpc/portergrpc.go @@ -8,6 +8,8 @@ import ( "get.porter.sh/porter/pkg/porter" "get.porter.sh/porter/pkg/secrets" secretsplugin "get.porter.sh/porter/pkg/secrets/pluginstore" + "get.porter.sh/porter/pkg/signing" + signingplugin "get.porter.sh/porter/pkg/signing/pluginstore" "get.porter.sh/porter/pkg/storage" storageplugin "get.porter.sh/porter/pkg/storage/pluginstore" "google.golang.org/grpc" @@ -30,7 +32,8 @@ func NewPorterServer(cfg *config.Config) (*PorterServer, error) { func (s *PorterServer) NewConnectionInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { storage := storage.NewPluginAdapter(storageplugin.NewStore(s.PorterConfig)) secretStorage := secrets.NewPluginAdapter(secretsplugin.NewStore(s.PorterConfig)) - p := porter.NewFor(s.PorterConfig, storage, secretStorage) + signer := signing.NewPluginAdapter(signingplugin.NewSigner(s.PorterConfig)) + p := porter.NewFor(s.PorterConfig, storage, secretStorage, signer) if _, err := p.Connect(ctx); err != nil { return nil, err } diff --git a/pkg/porter/helpers.go b/pkg/porter/helpers.go index a7f56f3f9..973a5f291 100644 --- a/pkg/porter/helpers.go +++ b/pkg/porter/helpers.go @@ -115,7 +115,7 @@ func (p *TestPorter) SetupIntegrationTest() context.Context { t := p.TestConfig.TestContext.T // Undo changes above to make a unit test friendly Porter, so we hit the host - p.Porter = NewFor(p.Config, p.TestStore, p.TestSecrets, p.Signatures) + p.Porter = NewFor(p.Config, p.TestStore, p.TestSecrets, p.Signer) // Run the test in a temp directory ctx, testDir, _ := p.TestConfig.SetupIntegrationTest() diff --git a/pkg/porter/install.go b/pkg/porter/install.go index 4b4e7714e..b8b88e86a 100644 --- a/pkg/porter/install.go +++ b/pkg/porter/install.go @@ -93,16 +93,15 @@ func (p *Porter) InstallBundle(ctx context.Context, opts InstallOptions) error { if err != nil { return err } - log.Infof("verifying signature for %s", ref.String()) + log.Debugf("verifying signature for %s", ref.String()) if !ok { - return fmt.Errorf("unable to get reference for bundle") + return log.Errorf("unable to get reference for bundle %s: %w", ref.String(), err) } - err = p.Signatures.Verify(ctx, ref.String()) + err = p.Signer.Verify(ctx, ref.String()) if err != nil { - log.Errorf("unable to verify signature %w", err) - return err + return log.Errorf("unable to verify signature: %w", err) } - log.Infof("signature verified for %s", ref.String()) + log.Debugf("signature verified for %s", ref.String()) } // Run install using the updated installation record diff --git a/pkg/porter/porter.go b/pkg/porter/porter.go index 2bd4c4f75..0da41f0d6 100644 --- a/pkg/porter/porter.go +++ b/pkg/porter/porter.go @@ -48,7 +48,7 @@ type Porter struct { CNAB cnabprovider.CNABProvider Secrets secrets.Store Storage storage.Provider - Signatures signing.Signer + Signer signing.Signer } // New porter client, initialized with useful defaults. @@ -56,8 +56,8 @@ func New() *Porter { c := config.New() storage := storage.NewPluginAdapter(storageplugin.NewStore(c)) secretStorage := secrets.NewPluginAdapter(secretsplugin.NewStore(c)) - signatures := signing.NewPluginAdapter(signingplugin.NewSigner(c)) - return NewFor(c, storage, secretStorage, signatures) + signer := signing.NewPluginAdapter(signingplugin.NewSigner(c)) + return NewFor(c, storage, secretStorage, signer) } func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store, signer signing.Signer) *Porter { @@ -68,6 +68,7 @@ func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store, credStorage := storage.NewCredentialStore(storageManager, secretStorage) paramStorage := storage.NewParameterStore(storageManager, secretStorage) sanitizerService := storage.NewSanitizer(paramStorage, secretStorage) + storageManager.Initialize(sanitizerService) // we have a bit of a dependency problem here that it would be great to figure out eventually return &Porter{ @@ -84,6 +85,7 @@ func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store, Plugins: plugins.NewPackageManager(c), CNAB: cnabprovider.NewRuntime(c, installationStorage, credStorage, secretStorage, sanitizerService), Sanitizer: sanitizerService, + Signer: signer, } } diff --git a/pkg/porter/publish.go b/pkg/porter/publish.go index 0eb33de06..2f3379916 100644 --- a/pkg/porter/publish.go +++ b/pkg/porter/publish.go @@ -216,22 +216,21 @@ func (p *Porter) publishFromFile(ctx context.Context, opts PublishOptions) error return err } - log.Infof("should I sign the bundle? %s", opts.SignBundle) if opts.SignBundle { - log.Infof("signing bundle %s", bundleRef.String()) + log.Debugf("signing bundle %s", bundleRef.String()) inImage, err := cnab.CalculateTemporaryImageTag(bundleRef.Reference) if err != nil { - return err + return log.Errorf("error calculation temporary image tag: %w", err) } - log.Infof("Signing invocation image %s.", inImage.String()) - err = p.Signatures.Sign(context.Background(), inImage.String()) + log.Debugf("Signing invocation image %s.", inImage.String()) + err = p.Signer.Sign(context.Background(), inImage.String()) if err != nil { - return err + return log.Errorf("error signing invocation image: %w", err) } - log.Infof("Signing bundle artifact %s.", bundleRef.Reference.String()) - p.Signatures.Sign(context.Background(), bundleRef.Reference.String()) + log.Debugf("Signing bundle artifact %s.", bundleRef.Reference.String()) + err = p.Signer.Sign(context.Background(), bundleRef.Reference.String()) if err != nil { - return err + return log.Errorf("error signing bundle artifact: %w", err) } } @@ -491,7 +490,3 @@ func (p *Porter) refreshCachedBundle(bundleRef cnab.BundleReference) error { } return nil } - -func (p *Porter) signBundle(bundleRef cnab.BundleReference) error { - return nil -} diff --git a/pkg/signing/plugins/cosign/signer.go b/pkg/signing/plugins/cosign/cosign.go similarity index 67% rename from pkg/signing/plugins/cosign/signer.go rename to pkg/signing/plugins/cosign/cosign.go index be5adbdca..26dd4f5cc 100644 --- a/pkg/signing/plugins/cosign/signer.go +++ b/pkg/signing/plugins/cosign/cosign.go @@ -14,15 +14,19 @@ var _ plugins.SigningProtocol = &Cosign{} // Signer implements an in-memory signer for testing. type Cosign struct { - PublicKey string - PrivateKey string + PublicKey string + PrivateKey string + RegistryMode string + Experimental bool } func NewSigner(c *portercontext.Context, cfg PluginConfig) *Cosign { s := &Cosign{ - PublicKey: cfg.PublicKey, - PrivateKey: cfg.PrivateKey, + PublicKey: cfg.PublicKey, + PrivateKey: cfg.PrivateKey, + RegistryMode: cfg.RegistryMode, + Experimental: cfg.Experimental, } return s @@ -49,7 +53,14 @@ func (s *Cosign) Sign(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() log.Infof("Cosign Signer is Signing %s", ref) - cmd := exec.Command("cosign", "sign", ref, "--tlog-upload=false", "--key", s.PrivateKey) + args := []string{"sign", ref, "--tlog-upload=false", "--key", s.PrivateKey, "--yes"} + if s.RegistryMode != "" { + args = append(args, "--registry-referrers-mode", s.RegistryMode) + } + cmd := exec.Command("cosign", args...) + if s.Experimental { + cmd.Env = append(cmd.Environ(), "COSIGN_EXPERIMENTAL=1") + } out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("%s: %w", string(out), err) @@ -64,7 +75,11 @@ func (s *Cosign) Verify(ctx context.Context, ref string) error { defer log.EndSpan() log.Infof("Mock Signer is Verifying %s", ref) - cmd := exec.Command("cosign", "verify", "--key", s.PublicKey, ref, "--insecure-ignore-tlog") + args := []string{"verify", "--key", s.PublicKey, ref, "--insecure-ignore-tlog"} + if s.RegistryMode == "oci-1-1" { + args = append(args, "--experimental-oci11") + } + cmd := exec.Command("cosign", args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("%s: %w", string(out), err) diff --git a/pkg/signing/plugins/cosign/plugin.go b/pkg/signing/plugins/cosign/plugin.go index cd3ef687f..2fc64feba 100644 --- a/pkg/signing/plugins/cosign/plugin.go +++ b/pkg/signing/plugins/cosign/plugin.go @@ -3,7 +3,6 @@ package cosign import ( "fmt" - "get.porter.sh/porter/pkg/config" "get.porter.sh/porter/pkg/portercontext" "get.porter.sh/porter/pkg/signing" "get.porter.sh/porter/pkg/signing/plugins" @@ -18,13 +17,14 @@ var _ plugins.SigningProtocol = &Plugin{} type PluginConfig struct { //theses are paths - PublicKey string `mapstructure:"publickey,omitempty"` - PrivateKey string `mapstructure:"privatekey,omitempty"` + PublicKey string `mapstructure:"publickey,omitempty"` + PrivateKey string `mapstructure:"privatekey,omitempty"` + RegistryMode string `mapstructure:"registrymode,omitempty"` + Experimental bool `mapstructure:"experimental,omitempty"` } // Plugin is the plugin wrapper for accessing secrets from a local filesystem. type Plugin struct { - config *config.Config signing.Signer } diff --git a/pkg/signing/plugins/mock/signer.go b/pkg/signing/plugins/mock/mock.go similarity index 95% rename from pkg/signing/plugins/mock/signer.go rename to pkg/signing/plugins/mock/mock.go index 6e65bf159..75c1b87eb 100644 --- a/pkg/signing/plugins/mock/signer.go +++ b/pkg/signing/plugins/mock/mock.go @@ -48,6 +48,7 @@ func (s *Signer) Sign(ctx context.Context, ref string) error { } func (s *Signer) Verify(ctx context.Context, ref string) error { + //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() diff --git a/pkg/signing/plugins/notation/signer.go b/pkg/signing/plugins/notation/notation.go similarity index 97% rename from pkg/signing/plugins/notation/signer.go rename to pkg/signing/plugins/notation/notation.go index 23f86e1a4..3a0abe4dc 100644 --- a/pkg/signing/plugins/notation/signer.go +++ b/pkg/signing/plugins/notation/notation.go @@ -31,7 +31,7 @@ func (s *Signer) Connect(ctx context.Context) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Debug("Running mock signer") + log.Debug("Running notation signer") return nil } diff --git a/pkg/signing/plugins/notation/plugin.go b/pkg/signing/plugins/notation/plugin.go index 21d69ca82..4cb4c96b0 100644 --- a/pkg/signing/plugins/notation/plugin.go +++ b/pkg/signing/plugins/notation/plugin.go @@ -3,7 +3,6 @@ package notation import ( "fmt" - "get.porter.sh/porter/pkg/config" "get.porter.sh/porter/pkg/portercontext" "get.porter.sh/porter/pkg/signing" "get.porter.sh/porter/pkg/signing/plugins" @@ -22,7 +21,6 @@ type PluginConfig struct { // Plugin is the plugin wrapper for accessing secrets from a local filesystem. type Plugin struct { - config *config.Config signing.Signer } From 96c696fcaf62bc0ccbe8245bfaf838d88771a709 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Thu, 11 Apr 2024 00:26:32 +0200 Subject: [PATCH 03/11] By default no signing plugin is enabled Signed-off-by: Kim Christensen --- pkg/config/datastore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/datastore.go b/pkg/config/datastore.go index b4eeb6bdc..3f0e23e6a 100644 --- a/pkg/config/datastore.go +++ b/pkg/config/datastore.go @@ -103,7 +103,7 @@ func DefaultDataStore() Data { RuntimeDriver: RuntimeDriverDocker, DefaultStoragePlugin: "mongodb-docker", DefaultSecretsPlugin: "host", - DefaultSigningPlugin: "mock", + DefaultSigningPlugin: "", Logs: LogConfig{Level: "info"}, Verbosity: DefaultVerbosity, } From 0c1d0d5a1620ee848dd2d68146702e0b635e9f52 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Sun, 14 Apr 2024 23:40:35 +0200 Subject: [PATCH 04/11] Support insecure registries Signed-off-by: Kim Christensen --- pkg/signing/plugins/cosign/cosign.go | 26 ++++++++++++++++-------- pkg/signing/plugins/cosign/plugin.go | 9 ++++---- pkg/signing/plugins/notation/notation.go | 18 ++++++++++++---- pkg/signing/plugins/notation/plugin.go | 3 ++- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/pkg/signing/plugins/cosign/cosign.go b/pkg/signing/plugins/cosign/cosign.go index 26dd4f5cc..54d2f4bc4 100644 --- a/pkg/signing/plugins/cosign/cosign.go +++ b/pkg/signing/plugins/cosign/cosign.go @@ -14,25 +14,27 @@ var _ plugins.SigningProtocol = &Cosign{} // Signer implements an in-memory signer for testing. type Cosign struct { - PublicKey string - PrivateKey string - RegistryMode string - Experimental bool + PublicKey string + PrivateKey string + RegistryMode string + Experimental bool + InsecureRegistry bool } func NewSigner(c *portercontext.Context, cfg PluginConfig) *Cosign { s := &Cosign{ - PublicKey: cfg.PublicKey, - PrivateKey: cfg.PrivateKey, - RegistryMode: cfg.RegistryMode, - Experimental: cfg.Experimental, + PublicKey: cfg.PublicKey, + PrivateKey: cfg.PrivateKey, + RegistryMode: cfg.RegistryMode, + Experimental: cfg.Experimental, + InsecureRegistry: cfg.InsecureRegistry, } return s } -// we should get the certificate... here? +// TODO: we should get the certificate... here? func (s *Cosign) Connect(ctx context.Context) error { //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) @@ -57,6 +59,9 @@ func (s *Cosign) Sign(ctx context.Context, ref string) error { if s.RegistryMode != "" { args = append(args, "--registry-referrers-mode", s.RegistryMode) } + if s.InsecureRegistry { + args = append(args, "--allow-insecure-registry") + } cmd := exec.Command("cosign", args...) if s.Experimental { cmd.Env = append(cmd.Environ(), "COSIGN_EXPERIMENTAL=1") @@ -79,6 +84,9 @@ func (s *Cosign) Verify(ctx context.Context, ref string) error { if s.RegistryMode == "oci-1-1" { args = append(args, "--experimental-oci11") } + if s.InsecureRegistry { + args = append(args, "--allow-insecure-registry") + } cmd := exec.Command("cosign", args...) out, err := cmd.CombinedOutput() if err != nil { diff --git a/pkg/signing/plugins/cosign/plugin.go b/pkg/signing/plugins/cosign/plugin.go index 2fc64feba..370e7bd4e 100644 --- a/pkg/signing/plugins/cosign/plugin.go +++ b/pkg/signing/plugins/cosign/plugin.go @@ -17,10 +17,11 @@ var _ plugins.SigningProtocol = &Plugin{} type PluginConfig struct { //theses are paths - PublicKey string `mapstructure:"publickey,omitempty"` - PrivateKey string `mapstructure:"privatekey,omitempty"` - RegistryMode string `mapstructure:"registrymode,omitempty"` - Experimental bool `mapstructure:"experimental,omitempty"` + PublicKey string `mapstructure:"publickey,omitempty"` + PrivateKey string `mapstructure:"privatekey,omitempty"` + RegistryMode string `mapstructure:"registrymode,omitempty"` + Experimental bool `mapstructure:"experimental,omitempty"` + InsecureRegistry bool `mapstructure:"insecureregistry,omitempty"` } // Plugin is the plugin wrapper for accessing secrets from a local filesystem. diff --git a/pkg/signing/plugins/notation/notation.go b/pkg/signing/plugins/notation/notation.go index 3a0abe4dc..fa9cfcae6 100644 --- a/pkg/signing/plugins/notation/notation.go +++ b/pkg/signing/plugins/notation/notation.go @@ -16,12 +16,14 @@ var _ plugins.SigningProtocol = &Signer{} type Signer struct { // Need the key we want to use - SigningKey string + SigningKey string + InsecureRegistry bool } func NewSigner(c *portercontext.Context, cfg PluginConfig) *Signer { s := &Signer{ - SigningKey: cfg.SigningKey, + SigningKey: cfg.SigningKey, + InsecureRegistry: cfg.InsecureRegistry, } return s } @@ -46,7 +48,11 @@ func (s *Signer) Sign(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - cmd := exec.Command("notation", "sign", ref, "--key", s.SigningKey) + args := []string{"sign", ref, "--key", s.SigningKey} + if s.InsecureRegistry { + args = append(args, "--insecure-registry") + } + cmd := exec.Command("notation", args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("%s: %w", string(out), err) @@ -60,7 +66,11 @@ func (s *Signer) Verify(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - cmd := exec.Command("notation", "verify", ref) + args := []string{"verify", ref} + if s.InsecureRegistry { + args = append(args, "--insecure-registry") + } + cmd := exec.Command("notation", args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("%s: %w", string(out), err) diff --git a/pkg/signing/plugins/notation/plugin.go b/pkg/signing/plugins/notation/plugin.go index 4cb4c96b0..03b9c9cfa 100644 --- a/pkg/signing/plugins/notation/plugin.go +++ b/pkg/signing/plugins/notation/plugin.go @@ -16,7 +16,8 @@ const PluginKey = plugins.PluginInterface + ".porter.notation" var _ plugins.SigningProtocol = &Plugin{} type PluginConfig struct { - SigningKey string `mapstructure:"key,omitempty"` + SigningKey string `mapstructure:"key,omitempty"` + InsecureRegistry bool `mapstructure:"insecureregistry,omitempty"` } // Plugin is the plugin wrapper for accessing secrets from a local filesystem. From 702ca2c11a9980571c7bc3170e7ab72adf6279a6 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Mon, 22 Apr 2024 23:59:57 +0200 Subject: [PATCH 05/11] Add integration tests Signed-off-by: Kim Christensen --- magefile.go | 51 ++++++- pkg/porter/install.go | 17 ++- pkg/signing/plugins/cosign/cosign.go | 6 +- tests/integration/signing_test.go | 138 ++++++++++++++++++ .../testdata/bundles/signing/porter.yaml | 29 ++++ .../signing/config/config-cosign.yaml | 16 ++ .../signing/config/config-notation.yaml | 15 ++ 7 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 tests/integration/signing_test.go create mode 100644 tests/integration/testdata/bundles/signing/porter.yaml create mode 100644 tests/integration/testdata/signing/config/config-cosign.yaml create mode 100644 tests/integration/testdata/signing/config/config-notation.yaml diff --git a/magefile.go b/magefile.go index 5d5f1532a..b6442d7ec 100644 --- a/magefile.go +++ b/magefile.go @@ -24,6 +24,9 @@ import ( "get.porter.sh/porter/tests/tester" mageci "github.com/carolynvs/magex/ci" "github.com/carolynvs/magex/mgx" + magepkg "github.com/carolynvs/magex/pkg" + "github.com/carolynvs/magex/pkg/archive" + "github.com/carolynvs/magex/pkg/downloads" "github.com/carolynvs/magex/shx" "github.com/carolynvs/magex/xplat" "github.com/magefile/mage/mg" @@ -567,7 +570,7 @@ func chmodRecursive(name string, mode os.FileMode) error { // Run integration tests (slow). func TestIntegration() { - mg.Deps(tests.EnsureTestCluster, copySchema, TryRegisterLocalHostAlias, BuildTestMixin, BuildTestPlugin) + mg.Deps(tests.EnsureTestCluster, copySchema, TryRegisterLocalHostAlias, BuildTestMixin, BuildTestPlugin, EnsureCosign, EnsureNotation) var run string runTest := os.Getenv("PORTER_RUN_TEST") @@ -723,3 +726,49 @@ func getPorterHome() string { func SetupDCO() error { return git.SetupDCO() } + +func EnsureCosign() { + if ok, _ := magepkg.IsCommandAvailable("cosign", "version", "v2.2.2"); ok { + return + } + + opts := downloads.DownloadOptions{ + UrlTemplate: "https://github.com/sigstore/cosign/releases/download/v{{.VERSION}}/cosign-{{.GOOS}}-{{.GOARCH}}{{.EXT}}", + Name: "buf", + Version: "2.2.2", + } + + if runtime.GOOS == "windows" { + opts.Ext = ".exe" + } + + err := downloads.DownloadToGopathBin(opts) + mgx.Must(err) +} + +func EnsureNotation() { + if ok, _ := magepkg.IsCommandAvailable("notation", "version", "1.1.0"); ok { + return + } + + target := "notation{{.EXT}}" + if runtime.GOOS == "windows" { + target = "notation.exe" + } + + opts := archive.DownloadArchiveOptions{ + DownloadOptions: downloads.DownloadOptions{ + UrlTemplate: "https://github.com/notaryproject/notation/releases/download/v{{.VERSION}}/notation_{{.VERSION}}_{{.GOOS}}_{{.GOARCH}}{{.EXT}}", + Name: "grpcurl", + Version: "1.1.0", + }, + ArchiveExtensions: map[string]string{ + "linux": ".tar.gz", + "darwin": ".tar.gz", + "windows": ".zip", + }, + TargetFileTemplate: target, + } + err := archive.DownloadToGopathBin(opts) + mgx.Must(err) +} diff --git a/pkg/porter/install.go b/pkg/porter/install.go index b8b88e86a..d49ef3f39 100644 --- a/pkg/porter/install.go +++ b/pkg/porter/install.go @@ -93,7 +93,7 @@ func (p *Porter) InstallBundle(ctx context.Context, opts InstallOptions) error { if err != nil { return err } - log.Debugf("verifying signature for %s", ref.String()) + log.Debugf("verifying bundle signature for %s", ref.String()) if !ok { return log.Errorf("unable to get reference for bundle %s: %w", ref.String(), err) } @@ -101,7 +101,20 @@ func (p *Porter) InstallBundle(ctx context.Context, opts InstallOptions) error { if err != nil { return log.Errorf("unable to verify signature: %w", err) } - log.Debugf("signature verified for %s", ref.String()) + log.Debugf("bundle signature verified for %s", ref.String()) + + bun, err := opts.GetOptions().GetBundleReference(ctx, p) + if err != nil { + return log.Errorf("unable to get bundle reference") + } + + invocationImage := bun.Definition.InvocationImages[0].Image + log.Debugf("verifying invocation image signature for %s", invocationImage) + err = p.Signer.Verify(ctx, invocationImage) + if err != nil { + return log.Errorf("unable to verify signature: %w", err) + } + log.Debugf("invocation image signature verified for %s", invocationImage) } // Run install using the updated installation record diff --git a/pkg/signing/plugins/cosign/cosign.go b/pkg/signing/plugins/cosign/cosign.go index 54d2f4bc4..a1ee26a2e 100644 --- a/pkg/signing/plugins/cosign/cosign.go +++ b/pkg/signing/plugins/cosign/cosign.go @@ -3,6 +3,7 @@ package cosign import ( "context" "fmt" + "os" "os/exec" "get.porter.sh/porter/pkg/portercontext" @@ -63,8 +64,9 @@ func (s *Cosign) Sign(ctx context.Context, ref string) error { args = append(args, "--allow-insecure-registry") } cmd := exec.Command("cosign", args...) + cmd.Env = append(cmd.Env, os.Environ()...) if s.Experimental { - cmd.Env = append(cmd.Environ(), "COSIGN_EXPERIMENTAL=1") + cmd.Env = append(cmd.Env, "COSIGN_EXPERIMENTAL=1") } out, err := cmd.CombinedOutput() if err != nil { @@ -79,7 +81,7 @@ func (s *Cosign) Verify(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Infof("Mock Signer is Verifying %s", ref) + log.Infof("Cosign Signer is Verifying %s", ref) args := []string{"verify", "--key", s.PublicKey, ref, "--insecure-ignore-tlog"} if s.RegistryMode == "oci-1-1" { args = append(args, "--experimental-oci11") diff --git a/tests/integration/signing_test.go b/tests/integration/signing_test.go new file mode 100644 index 000000000..f2d0174cd --- /dev/null +++ b/tests/integration/signing_test.go @@ -0,0 +1,138 @@ +//go:build integration + +package integration + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "testing" + + "get.porter.sh/porter/pkg/cnab" + "get.porter.sh/porter/tests/tester" + "github.com/carolynvs/magex/shx" + "github.com/docker/distribution/reference" + "github.com/google/go-containerregistry/pkg/crane" + "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/require" +) + +func TestCosign(t *testing.T) { + t.Parallel() + + testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-cosign.yaml") + require.NoError(t, err, "tester.NewTest failed") + defer testr.Close() + reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: true}) + defer reg.Close() + ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/cosign:v1.0.0", reg.String())) + + cmd := shx.Command("cosign", "generate-key-pair").Env("COSIGN_PASSWORD='test'").In(testr.PorterHomeDir) + err = cmd.RunE() + require.NoError(t, err, "Generate cosign key pair failed") + _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { + pc.Args("publish", "--sign-bundle", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) + pc.Env("COSIGN_PASSWORD='test'") + }) + require.NoError(t, err, "Publish failed") + + ref = toRefWithDigest(t, ref) + invocationImageRef := getInvocationImageDigest(t, output) + + _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry") + fmt.Println(output) + require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) + require.Contains(t, output, fmt.Sprintf("invocation image signature verified for %s", invocationImageRef.String())) +} + +func TestNotation(t *testing.T) { + t.Parallel() + + testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-notation.yaml") + require.NoError(t, err, "tester.NewTest failed") + defer testr.Close() + reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: false}) + defer reg.Close() + ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/cosign:v1.0.0", reg.String())) + + cmd := shx.Command("notation", "cert", "generate-test", "porter-test.org") + err = cmd.RunE() + require.NoError(t, err, "Generate notation certificate failed") + defer func() { + output, err := shx.Command("notation", "key", "ls").Output() + require.NoError(t, err) + keyRegex := regexp.MustCompile(`(/.+porter-test\.org\.key)`) + keyMatches := keyRegex.FindAllStringSubmatch(output, -1) + require.Len(t, keyMatches, 1) + crtRegex := regexp.MustCompile(`key\s+(/.+porter-test\.org\.crt)`) + crtMatches := crtRegex.FindAllStringSubmatch(output, -1) + require.Len(t, crtMatches, 1) + err = shx.Command("notation", "key", "delete", "porter-test.org").RunV() + require.NoError(t, err) + err = shx.Command("notation", "cert", "delete", "--type", "ca", "--store", "porter-test.org", "porter-test.org.crt", "--yes").RunV() + require.NoError(t, err) + err = os.Remove(keyMatches[0][1]) + require.NoError(t, err) + err = os.Remove(crtMatches[0][1]) + require.NoError(t, err) + }() + trustPolicy := ` + { + "version": "1.0", + "trustPolicies": [ + { + "name": "porter-test-images", + "registryScopes": [ "*" ], + "signatureVerification": { + "level" : "strict" + }, + "trustStores": [ "ca:porter-test.org" ], + "trustedIdentities": [ + "*" + ] + } + ] + }` + trustPolicyPath := filepath.Join(testr.PorterHomeDir, "trustpolicy.json") + err = os.WriteFile(trustPolicyPath, []byte(trustPolicy), 0644) + require.NoError(t, err, "Creation of trust policy failed") + err = shx.Command("notation", "policy", "import", trustPolicyPath).RunE() + require.NoError(t, err, "importing trust policy failed") + + _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { + pc.Args("publish", "--sign-bundle", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) + }) + require.NoError(t, err, "Publish failed") + + ref = toRefWithDigest(t, ref) + invocationImageRef := getInvocationImageDigest(t, output) + + _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry") + fmt.Println(output) + require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) + require.Contains(t, output, fmt.Sprintf("invocation image signature verified for %s", invocationImageRef.String())) +} + +func toRefWithDigest(t *testing.T, ref cnab.OCIReference) cnab.OCIReference { + desc, err := crane.Head(ref.String(), crane.Insecure) + require.NoError(t, err) + ref.Named = reference.TrimNamed(ref.Named) + ref, err = ref.WithDigest(digest.Digest(desc.Digest.String())) + require.NoError(t, err) + return ref +} + +func getInvocationImageDigest(t *testing.T, output string) cnab.OCIReference { + r := regexp.MustCompile(`(?m:^Signing invocation image (localhost:\d+/cosign:porter-[0-9a-z]+)\.)`) + matches := r.FindAllStringSubmatch(output, -1) + require.Len(t, matches, 1) + invocationImageRefString := matches[0][1] + desc, err := crane.Head(invocationImageRefString, crane.Insecure) + require.NoError(t, err) + ref := cnab.MustParseOCIReference(invocationImageRefString) + ref.Named = reference.TrimNamed(ref.Named) + ref, err = ref.WithDigest(digest.Digest(desc.Digest.String())) + require.NoError(t, err) + return ref +} diff --git a/tests/integration/testdata/bundles/signing/porter.yaml b/tests/integration/testdata/bundles/signing/porter.yaml new file mode 100644 index 000000000..72481b453 --- /dev/null +++ b/tests/integration/testdata/bundles/signing/porter.yaml @@ -0,0 +1,29 @@ +schemaVersion: 1.0.0-alpha.1 +name: mybun +version: 0.1.0 +description: "An example Porter configuration" +registry: "localhost:5000" + +mixins: + - exec + +install: + - exec: + description: "Install Hello World" + command: echo + arguments: + - install + +upgrade: + - exec: + description: "Upgrade Hello World" + command: echo + arguments: + - upgrade + +uninstall: + - exec: + description: "Uninstall Hello World" + command: echo + arguments: + - uninstall diff --git a/tests/integration/testdata/signing/config/config-cosign.yaml b/tests/integration/testdata/signing/config/config-cosign.yaml new file mode 100644 index 000000000..5ce44e16e --- /dev/null +++ b/tests/integration/testdata/signing/config/config-cosign.yaml @@ -0,0 +1,16 @@ +default-signer: cosign +default-storage: testdb + +signers: + - name: cosign + plugin: cosign + config: + publickey: ${env.PORTER_HOME}/cosign.pub + privatekey: ${env.PORTER_HOME}/cosign.key + insecureregistry: true + +storage: + - name: testdb + plugin: mongodb + config: + url: mongodb://localhost:27017/${env.PORTER_TEST_DB_NAME}?connect=direct diff --git a/tests/integration/testdata/signing/config/config-notation.yaml b/tests/integration/testdata/signing/config/config-notation.yaml new file mode 100644 index 000000000..3f47b36fb --- /dev/null +++ b/tests/integration/testdata/signing/config/config-notation.yaml @@ -0,0 +1,15 @@ +default-signer: notation +default-storage: testdb + +signers: + - name: notation + plugin: notation + config: + key: porter-test.org + insecureregistry: true + +storage: + - name: testdb + plugin: mongodb + config: + url: mongodb://localhost:27017/${env.PORTER_TEST_DB_NAME}?connect=direct From 98b7ebeaa13278e270801a819e184245ecc31814 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Tue, 23 Apr 2024 01:16:47 +0200 Subject: [PATCH 06/11] Fix lint Signed-off-by: Kim Christensen --- pkg/signing/plugins/mock/mock.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/signing/plugins/mock/mock.go b/pkg/signing/plugins/mock/mock.go index 75c1b87eb..d0f79240a 100644 --- a/pkg/signing/plugins/mock/mock.go +++ b/pkg/signing/plugins/mock/mock.go @@ -24,6 +24,7 @@ func NewSigner() *Signer { } func (s *Signer) Connect(ctx context.Context) error { + //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() From 3f0f41a6144d8ca8a711d666a08cee96709a4f08 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 24 Apr 2024 10:09:22 +0200 Subject: [PATCH 07/11] Fix wrong name Signed-off-by: Kim Christensen --- magefile.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/magefile.go b/magefile.go index b6442d7ec..c713f2742 100644 --- a/magefile.go +++ b/magefile.go @@ -734,7 +734,7 @@ func EnsureCosign() { opts := downloads.DownloadOptions{ UrlTemplate: "https://github.com/sigstore/cosign/releases/download/v{{.VERSION}}/cosign-{{.GOOS}}-{{.GOARCH}}{{.EXT}}", - Name: "buf", + Name: "cosign", Version: "2.2.2", } @@ -759,7 +759,7 @@ func EnsureNotation() { opts := archive.DownloadArchiveOptions{ DownloadOptions: downloads.DownloadOptions{ UrlTemplate: "https://github.com/notaryproject/notation/releases/download/v{{.VERSION}}/notation_{{.VERSION}}_{{.GOOS}}_{{.GOARCH}}{{.EXT}}", - Name: "grpcurl", + Name: "notation", Version: "1.1.0", }, ArchiveExtensions: map[string]string{ From ddd49d0e0b1f89e2fb16182b8e657c4afd6b2e60 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 1 May 2024 00:13:42 +0200 Subject: [PATCH 08/11] Close plugin connection Signed-off-by: Kim Christensen --- pkg/porter/porter.go | 5 +++++ pkg/signing/pluginstore/signer.go | 8 ++++++++ pkg/signing/signer.go | 2 ++ 3 files changed, 15 insertions(+) diff --git a/pkg/porter/porter.go b/pkg/porter/porter.go index 0da41f0d6..7d1ff9876 100644 --- a/pkg/porter/porter.go +++ b/pkg/porter/porter.go @@ -139,6 +139,11 @@ func (p *Porter) Close() error { bigErr = multierror.Append(bigErr, err) } + err = p.Signer.Close() + if err != nil { + bigErr = multierror.Append(bigErr, err) + } + return bigErr.ErrorOrNil() } diff --git a/pkg/signing/pluginstore/signer.go b/pkg/signing/pluginstore/signer.go index d2ed83482..390370c59 100644 --- a/pkg/signing/pluginstore/signer.go +++ b/pkg/signing/pluginstore/signer.go @@ -111,3 +111,11 @@ func (s *Signer) Connect(ctx context.Context) error { return nil } + +func (s *Signer) Close() error { + if s.conn != nil { + s.conn.Close(context.Background()) + s.conn = nil + } + return nil +} diff --git a/pkg/signing/signer.go b/pkg/signing/signer.go index 599135d26..a4b50e6bf 100644 --- a/pkg/signing/signer.go +++ b/pkg/signing/signer.go @@ -5,6 +5,8 @@ import "context" // Signer is an interface for signing and verifying Porter // bundles and invocation images. type Signer interface { + Close() error + // Sign generates a signature for the specified reference, which // can be a Porter bundle or an invocation image. Sign(ctx context.Context, ref string) error From 203f9165422d812e7d41532bcb95d4d31d644213 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 1 May 2024 00:15:10 +0200 Subject: [PATCH 09/11] Remove Close from the signer implementations Signed-off-by: Kim Christensen --- pkg/signing/plugins/cosign/cosign.go | 5 ----- pkg/signing/plugins/notation/notation.go | 5 ----- 2 files changed, 10 deletions(-) diff --git a/pkg/signing/plugins/cosign/cosign.go b/pkg/signing/plugins/cosign/cosign.go index a1ee26a2e..9d11cf494 100644 --- a/pkg/signing/plugins/cosign/cosign.go +++ b/pkg/signing/plugins/cosign/cosign.go @@ -46,11 +46,6 @@ func (s *Cosign) Connect(ctx context.Context) error { return nil } -// Close implements the Close method on the signing plugins' interface. -func (s *Cosign) Close() error { - return nil -} - func (s *Cosign) Sign(ctx context.Context, ref string) error { //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) diff --git a/pkg/signing/plugins/notation/notation.go b/pkg/signing/plugins/notation/notation.go index fa9cfcae6..433cdd400 100644 --- a/pkg/signing/plugins/notation/notation.go +++ b/pkg/signing/plugins/notation/notation.go @@ -38,11 +38,6 @@ func (s *Signer) Connect(ctx context.Context) error { return nil } -// Close implements the Close method on the signing plugins' interface. -func (s *Signer) Close() error { - return nil -} - func (s *Signer) Sign(ctx context.Context, ref string) error { //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) From fb6eaa0240788ecbe5b773e27aadab52d1cf07b9 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 1 May 2024 00:40:37 +0200 Subject: [PATCH 10/11] Make Connect function check if signing tools are available Signed-off-by: Kim Christensen --- pkg/signing/plugin_adapter.go | 4 + pkg/signing/plugins/cosign/cosign.go | 6 +- pkg/signing/plugins/notation/notation.go | 5 +- .../plugins/proto/signing_protocol.pb.go | 174 ++++++++++++++---- .../plugins/proto/signing_protocol.proto | 5 + .../plugins/proto/signing_protocol_grpc.pb.go | 36 ++++ pkg/signing/plugins/signing_protocol.go | 2 + pkg/signing/pluginstore/grpc.go | 14 ++ pkg/signing/pluginstore/signer.go | 4 + pkg/signing/signer.go | 2 + 10 files changed, 217 insertions(+), 35 deletions(-) diff --git a/pkg/signing/plugin_adapter.go b/pkg/signing/plugin_adapter.go index cef3f4241..1b116a716 100644 --- a/pkg/signing/plugin_adapter.go +++ b/pkg/signing/plugin_adapter.go @@ -34,3 +34,7 @@ func (a PluginAdapter) Sign(ctx context.Context, ref string) error { func (a PluginAdapter) Verify(ctx context.Context, ref string) error { return a.plugin.Verify(ctx, ref) } + +func (a PluginAdapter) Connect(ctx context.Context) error { + return a.plugin.Connect(ctx) +} diff --git a/pkg/signing/plugins/cosign/cosign.go b/pkg/signing/plugins/cosign/cosign.go index 9d11cf494..6dbe81f8d 100644 --- a/pkg/signing/plugins/cosign/cosign.go +++ b/pkg/signing/plugins/cosign/cosign.go @@ -2,6 +2,7 @@ package cosign import ( "context" + "errors" "fmt" "os" "os/exec" @@ -35,13 +36,14 @@ func NewSigner(c *portercontext.Context, cfg PluginConfig) *Cosign { return s } -// TODO: we should get the certificate... here? func (s *Cosign) Connect(ctx context.Context) error { //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Debug("Running cosign signer") + if err := exec.Command("cosign", "version").Run(); err != nil { + return errors.New("cosign was not found") + } return nil } diff --git a/pkg/signing/plugins/notation/notation.go b/pkg/signing/plugins/notation/notation.go index 433cdd400..66cea612c 100644 --- a/pkg/signing/plugins/notation/notation.go +++ b/pkg/signing/plugins/notation/notation.go @@ -2,6 +2,7 @@ package notation import ( "context" + "errors" "fmt" "os/exec" @@ -33,7 +34,9 @@ func (s *Signer) Connect(ctx context.Context) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Debug("Running notation signer") + if err := exec.Command("notation", "version").Run(); err != nil { + return errors.New("notation was not found") + } return nil } diff --git a/pkg/signing/plugins/proto/signing_protocol.pb.go b/pkg/signing/plugins/proto/signing_protocol.pb.go index f9c0c1775..a18e5bac4 100644 --- a/pkg/signing/plugins/proto/signing_protocol.pb.go +++ b/pkg/signing/plugins/proto/signing_protocol.pb.go @@ -114,6 +114,44 @@ func (x *VerifyRequest) GetRef() string { return "" } +type ConnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ConnectRequest) Reset() { + *x = ConnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectRequest) ProtoMessage() {} + +func (x *ConnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectRequest.ProtoReflect.Descriptor instead. +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{2} +} + type SignResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -123,7 +161,7 @@ type SignResponse struct { func (x *SignResponse) Reset() { *x = SignResponse{} if protoimpl.UnsafeEnabled { - mi := &file_signing_protocol_proto_msgTypes[2] + mi := &file_signing_protocol_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -136,7 +174,7 @@ func (x *SignResponse) String() string { func (*SignResponse) ProtoMessage() {} func (x *SignResponse) ProtoReflect() protoreflect.Message { - mi := &file_signing_protocol_proto_msgTypes[2] + mi := &file_signing_protocol_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -149,7 +187,7 @@ func (x *SignResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SignResponse.ProtoReflect.Descriptor instead. func (*SignResponse) Descriptor() ([]byte, []int) { - return file_signing_protocol_proto_rawDescGZIP(), []int{2} + return file_signing_protocol_proto_rawDescGZIP(), []int{3} } type VerifyResponse struct { @@ -161,7 +199,7 @@ type VerifyResponse struct { func (x *VerifyResponse) Reset() { *x = VerifyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_signing_protocol_proto_msgTypes[3] + mi := &file_signing_protocol_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -174,7 +212,7 @@ func (x *VerifyResponse) String() string { func (*VerifyResponse) ProtoMessage() {} func (x *VerifyResponse) ProtoReflect() protoreflect.Message { - mi := &file_signing_protocol_proto_msgTypes[3] + mi := &file_signing_protocol_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -187,7 +225,45 @@ func (x *VerifyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyResponse.ProtoReflect.Descriptor instead. func (*VerifyResponse) Descriptor() ([]byte, []int) { - return file_signing_protocol_proto_rawDescGZIP(), []int{3} + return file_signing_protocol_proto_rawDescGZIP(), []int{4} +} + +type ConnectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ConnectResponse) Reset() { + *x = ConnectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_signing_protocol_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectResponse) ProtoMessage() {} + +func (x *ConnectResponse) ProtoReflect() protoreflect.Message { + mi := &file_signing_protocol_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectResponse.ProtoReflect.Descriptor instead. +func (*ConnectResponse) Descriptor() ([]byte, []int) { + return file_signing_protocol_proto_rawDescGZIP(), []int{5} } var File_signing_protocol_proto protoreflect.FileDescriptor @@ -199,21 +275,27 @@ var file_signing_protocol_proto_rawDesc = []byte{ 0x12, 0x10, 0x0a, 0x03, 0x52, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x22, 0x21, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x52, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x52, 0x65, 0x66, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x81, 0x01, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, - 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x33, 0x0a, 0x04, 0x53, - 0x69, 0x67, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x53, 0x69, - 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x39, 0x0a, 0x06, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, - 0x65, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2e, 0x73, 0x68, 0x2f, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2f, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x03, 0x52, 0x65, 0x66, 0x22, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xbf, 0x01, 0x0a, + 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x12, 0x33, 0x0a, 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, + 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x73, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3c, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x17, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, + 0x5a, 0x2e, 0x67, 0x65, 0x74, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2e, 0x73, 0x68, 0x2f, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -228,20 +310,24 @@ func file_signing_protocol_proto_rawDescGZIP() []byte { return file_signing_protocol_proto_rawDescData } -var file_signing_protocol_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_signing_protocol_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_signing_protocol_proto_goTypes = []interface{}{ - (*SignRequest)(nil), // 0: plugins.SignRequest - (*VerifyRequest)(nil), // 1: plugins.VerifyRequest - (*SignResponse)(nil), // 2: plugins.SignResponse - (*VerifyResponse)(nil), // 3: plugins.VerifyResponse + (*SignRequest)(nil), // 0: plugins.SignRequest + (*VerifyRequest)(nil), // 1: plugins.VerifyRequest + (*ConnectRequest)(nil), // 2: plugins.ConnectRequest + (*SignResponse)(nil), // 3: plugins.SignResponse + (*VerifyResponse)(nil), // 4: plugins.VerifyResponse + (*ConnectResponse)(nil), // 5: plugins.ConnectResponse } var file_signing_protocol_proto_depIdxs = []int32{ 0, // 0: plugins.SigningProtocol.Sign:input_type -> plugins.SignRequest 1, // 1: plugins.SigningProtocol.Verify:input_type -> plugins.VerifyRequest - 2, // 2: plugins.SigningProtocol.Sign:output_type -> plugins.SignResponse - 3, // 3: plugins.SigningProtocol.Verify:output_type -> plugins.VerifyResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type + 2, // 2: plugins.SigningProtocol.Connect:input_type -> plugins.ConnectRequest + 3, // 3: plugins.SigningProtocol.Sign:output_type -> plugins.SignResponse + 4, // 4: plugins.SigningProtocol.Verify:output_type -> plugins.VerifyResponse + 5, // 5: plugins.SigningProtocol.Connect:output_type -> plugins.ConnectResponse + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -278,7 +364,7 @@ func file_signing_protocol_proto_init() { } } file_signing_protocol_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignResponse); i { + switch v := v.(*ConnectRequest); i { case 0: return &v.state case 1: @@ -290,6 +376,18 @@ func file_signing_protocol_proto_init() { } } file_signing_protocol_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_signing_protocol_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VerifyResponse); i { case 0: return &v.state @@ -301,6 +399,18 @@ func file_signing_protocol_proto_init() { return nil } } + file_signing_protocol_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -308,7 +418,7 @@ func file_signing_protocol_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_signing_protocol_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/signing/plugins/proto/signing_protocol.proto b/pkg/signing/plugins/proto/signing_protocol.proto index bd6412e5d..cf728cfdb 100644 --- a/pkg/signing/plugins/proto/signing_protocol.proto +++ b/pkg/signing/plugins/proto/signing_protocol.proto @@ -11,11 +11,16 @@ message VerifyRequest { string Ref = 1; } +message ConnectRequest {} + message SignResponse {} message VerifyResponse {} +message ConnectResponse {} + service SigningProtocol { rpc Sign(SignRequest) returns (SignResponse); rpc Verify(VerifyRequest) returns (VerifyResponse); + rpc Connect(ConnectRequest) returns (ConnectResponse); } \ No newline at end of file diff --git a/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go b/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go index 974540951..3d5cb293e 100644 --- a/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go +++ b/pkg/signing/plugins/proto/signing_protocol_grpc.pb.go @@ -24,6 +24,7 @@ const _ = grpc.SupportPackageIsVersion7 type SigningProtocolClient interface { Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*SignResponse, error) Verify(ctx context.Context, in *VerifyRequest, opts ...grpc.CallOption) (*VerifyResponse, error) + Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) } type signingProtocolClient struct { @@ -52,12 +53,22 @@ func (c *signingProtocolClient) Verify(ctx context.Context, in *VerifyRequest, o return out, nil } +func (c *signingProtocolClient) Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) { + out := new(ConnectResponse) + err := c.cc.Invoke(ctx, "/plugins.SigningProtocol/Connect", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SigningProtocolServer is the server API for SigningProtocol service. // All implementations must embed UnimplementedSigningProtocolServer // for forward compatibility type SigningProtocolServer interface { Sign(context.Context, *SignRequest) (*SignResponse, error) Verify(context.Context, *VerifyRequest) (*VerifyResponse, error) + Connect(context.Context, *ConnectRequest) (*ConnectResponse, error) mustEmbedUnimplementedSigningProtocolServer() } @@ -71,6 +82,9 @@ func (UnimplementedSigningProtocolServer) Sign(context.Context, *SignRequest) (* func (UnimplementedSigningProtocolServer) Verify(context.Context, *VerifyRequest) (*VerifyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Verify not implemented") } +func (UnimplementedSigningProtocolServer) Connect(context.Context, *ConnectRequest) (*ConnectResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Connect not implemented") +} func (UnimplementedSigningProtocolServer) mustEmbedUnimplementedSigningProtocolServer() {} // UnsafeSigningProtocolServer may be embedded to opt out of forward compatibility for this service. @@ -120,6 +134,24 @@ func _SigningProtocol_Verify_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _SigningProtocol_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConnectRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SigningProtocolServer).Connect(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugins.SigningProtocol/Connect", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SigningProtocolServer).Connect(ctx, req.(*ConnectRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SigningProtocol_ServiceDesc is the grpc.ServiceDesc for SigningProtocol service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -135,6 +167,10 @@ var SigningProtocol_ServiceDesc = grpc.ServiceDesc{ MethodName: "Verify", Handler: _SigningProtocol_Verify_Handler, }, + { + MethodName: "Connect", + Handler: _SigningProtocol_Connect_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "signing_protocol.proto", diff --git a/pkg/signing/plugins/signing_protocol.go b/pkg/signing/plugins/signing_protocol.go index e6aeb335e..24e8d29f0 100644 --- a/pkg/signing/plugins/signing_protocol.go +++ b/pkg/signing/plugins/signing_protocol.go @@ -5,6 +5,8 @@ import "context" // SigningProtocol is the interface that signing plugins must implement. // This defines the protocol used to communicate with signing plugins. type SigningProtocol interface { + Connect(ctx context.Context) error + // Resolve a secret value from a secret store // - ref is OCI reference to verify Sign(ctx context.Context, ref string) error diff --git a/pkg/signing/pluginstore/grpc.go b/pkg/signing/pluginstore/grpc.go index af2dc7f02..34127794f 100644 --- a/pkg/signing/pluginstore/grpc.go +++ b/pkg/signing/pluginstore/grpc.go @@ -39,6 +39,12 @@ func (m *GClient) Verify(ctx context.Context, ref string) error { return err } +func (m *GClient) Connect(ctx context.Context) error { + req := &proto.ConnectRequest{} + _, err := m.client.Connect(ctx, req) + return err +} + // GServer is a gRPC wrapper around a SecretsProtocol plugin type GServer struct { c *portercontext.Context @@ -65,3 +71,11 @@ func (m *GServer) Verify(ctx context.Context, request *proto.VerifyRequest) (*pr } return &proto.VerifyResponse{}, nil } + +func (m *GServer) Connect(ctx context.Context, request *proto.ConnectRequest) (*proto.ConnectResponse, error) { + err := m.impl.Connect(ctx) + if err != nil { + return nil, err + } + return &proto.ConnectResponse{}, nil +} diff --git a/pkg/signing/pluginstore/signer.go b/pkg/signing/pluginstore/signer.go index 390370c59..e564fcb2d 100644 --- a/pkg/signing/pluginstore/signer.go +++ b/pkg/signing/pluginstore/signer.go @@ -109,6 +109,10 @@ func (s *Signer) Connect(ctx context.Context) error { } s.plugin = store + if err = s.plugin.Connect(ctx); err != nil { + return err + } + return nil } diff --git a/pkg/signing/signer.go b/pkg/signing/signer.go index a4b50e6bf..d4cc42792 100644 --- a/pkg/signing/signer.go +++ b/pkg/signing/signer.go @@ -13,4 +13,6 @@ type Signer interface { // Verify attempts to verify a signature for the specified // reference, which can be a Porter bundle or an invocation image. Verify(ctx context.Context, ref string) error + // TODO + Connect(ctx context.Context) error } From 612d45edb8c85155797f36929924582f7eefaa27 Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Wed, 1 May 2024 13:24:01 +0200 Subject: [PATCH 11/11] Do not expose mock signing plugin for anything but tests Signed-off-by: Kim Christensen --- pkg/porter/internal_plugins.go | 8 -------- pkg/signing/helpers.go | 12 +++++++++--- pkg/signing/plugins/mock/mock.go | 14 ++++---------- pkg/signing/plugins/mock/plugin.go | 23 ----------------------- 4 files changed, 13 insertions(+), 44 deletions(-) delete mode 100644 pkg/signing/plugins/mock/plugin.go diff --git a/pkg/porter/internal_plugins.go b/pkg/porter/internal_plugins.go index bf2de98dc..e839622e1 100644 --- a/pkg/porter/internal_plugins.go +++ b/pkg/porter/internal_plugins.go @@ -15,7 +15,6 @@ import ( "get.porter.sh/porter/pkg/secrets/plugins/host" signingplugins "get.porter.sh/porter/pkg/signing/plugins" "get.porter.sh/porter/pkg/signing/plugins/cosign" - "get.porter.sh/porter/pkg/signing/plugins/mock" "get.porter.sh/porter/pkg/signing/plugins/notation" storageplugins "get.porter.sh/porter/pkg/storage/plugins" "get.porter.sh/porter/pkg/storage/plugins/mongodb" @@ -147,13 +146,6 @@ func getInternalPlugins() map[string]InternalPlugin { return mongodb_docker.NewPlugin(c.Context, pluginCfg) }, }, - mock.PluginKey: { - Interface: signingplugins.PluginInterface, - ProtocolVersion: signingplugins.PluginProtocolVersion, - Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) { - return mock.NewPlugin(c.Context, pluginCfg) - }, - }, notation.PluginKey: { Interface: signingplugins.PluginInterface, ProtocolVersion: signingplugins.PluginProtocolVersion, diff --git a/pkg/signing/helpers.go b/pkg/signing/helpers.go index e8e789c15..697ee1b5b 100644 --- a/pkg/signing/helpers.go +++ b/pkg/signing/helpers.go @@ -1,13 +1,19 @@ package signing +import "get.porter.sh/porter/pkg/signing/plugins/mock" + var _ Signer = &TestSigningProvider{} type TestSigningProvider struct { PluginAdapter - //TODO: add a test signer here + + signer *mock.Signer } func NewTestSigningProvider() TestSigningProvider { - // TODO: implement this - return TestSigningProvider{} + signer := mock.NewSigner() + return TestSigningProvider{ + PluginAdapter: NewPluginAdapter(signer), + signer: signer, + } } diff --git a/pkg/signing/plugins/mock/mock.go b/pkg/signing/plugins/mock/mock.go index d0f79240a..9effbb300 100644 --- a/pkg/signing/plugins/mock/mock.go +++ b/pkg/signing/plugins/mock/mock.go @@ -27,14 +27,6 @@ func (s *Signer) Connect(ctx context.Context) error { //lint:ignore SA4006 ignore unused ctx for now ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - - log.Debug("Running mock signer") - - return nil -} - -// Close implements the Close method on the signing plugins' interface. -func (s *Signer) Close() error { return nil } @@ -43,7 +35,6 @@ func (s *Signer) Sign(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Infof("Mock Signer is Signing %s", ref) s.Signatures[ref] = b64.StdEncoding.EncodeToString([]byte(ref)) return nil } @@ -53,6 +44,9 @@ func (s *Signer) Verify(ctx context.Context, ref string) error { ctx, log := tracing.StartSpan(ctx) defer log.EndSpan() - log.Infof("Mock Signer is Verifying %s", ref) + if _, ok := s.Signatures[ref]; !ok { + return log.Errorf("%s is not signed", ref) + } + return nil } diff --git a/pkg/signing/plugins/mock/plugin.go b/pkg/signing/plugins/mock/plugin.go deleted file mode 100644 index 5f432e69c..000000000 --- a/pkg/signing/plugins/mock/plugin.go +++ /dev/null @@ -1,23 +0,0 @@ -package mock - -import ( - "get.porter.sh/porter/pkg/portercontext" - "get.porter.sh/porter/pkg/signing" - "get.porter.sh/porter/pkg/signing/plugins" - "get.porter.sh/porter/pkg/signing/pluginstore" - "github.com/hashicorp/go-plugin" -) - -const PluginKey = plugins.PluginInterface + ".porter.mock" - -var _ plugins.SigningProtocol = &Plugin{} - -// Plugin is the plugin wrapper for accessing secrets from a local filesystem. -type Plugin struct { - signing.Signer -} - -func NewPlugin(c *portercontext.Context, rawCfg interface{}) (plugin.Plugin, error) { - impl := NewSigner() - return pluginstore.NewPlugin(c, impl), nil -}