Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
[WIP: do-not-merge] Preliminary support for configuring TLS channel b…
Browse files Browse the repository at this point in the history
…etween framework and plugin

- Snap REST API is accepting additional files upon PluginLoad request;
- Snap PluginLoad accepts paths to plugin certificate and key to be passed inline with request;
- Snap embeds the certificate paths into plugin runtime arguments, so that plugin
may make use of it (fields CertPath, KeyPath);
- Snaptel extended to accept additional options: --plugin-cert and --plugin-key; arguments to those
options are packed into a request for Snap REST API.
  • Loading branch information
marcin-ol committed May 2, 2017
1 parent 65090cf commit 6d6b411
Show file tree
Hide file tree
Showing 17 changed files with 417 additions and 99 deletions.
4 changes: 4 additions & 0 deletions cmd/snaptel/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ var (
Action: loadPlugin,
Flags: []cli.Flag{
flPluginAsc,
flPluginCert,
flPluginKey,
},
},
{
Expand All @@ -121,6 +123,8 @@ var (
flPluginType,
flPluginName,
flPluginVersion,
flPluginCert,
flPluginKey,
},
},
{
Expand Down
8 changes: 8 additions & 0 deletions cmd/snaptel/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ var (
Name: "plugin-asc, a",
Usage: "The plugin asc",
}
flPluginCert = cli.StringFlag{
Name: "plugin-cert, c",
Usage: "The plugin cert",
}
flPluginKey = cli.StringFlag{
Name: "plugin-key, k",
Usage: "The plugin key",
}
flPluginType = cli.StringFlag{
Name: "plugin-type, t",
Usage: "The plugin type",
Expand Down
40 changes: 39 additions & 1 deletion cmd/snaptel/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package main

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
Expand All @@ -35,7 +36,7 @@ func loadPlugin(ctx *cli.Context) error {
pAsc := ctx.String("plugin-asc")
var paths []string
if len(ctx.Args()) != 1 {
return newUsageError("Incorrect usage", ctx)
return newUsageError("Incorrect usage:", ctx)
}
paths = append(paths, ctx.Args().First())
if pAsc != "" {
Expand All @@ -44,6 +45,9 @@ func loadPlugin(ctx *cli.Context) error {
}
paths = append(paths, pAsc)
}
if paths, err = storeTLSPaths(ctx, paths); err != nil {
return err
}
r := pClient.LoadPlugin(paths)
if r.Err != nil {
if r.Err.Fields()["error"] != nil {
Expand Down Expand Up @@ -113,6 +117,9 @@ func swapPlugins(ctx *cli.Context) error {
}
paths = append(paths, pAsc)
}
if paths, err = storeTLSPaths(ctx, paths); err != nil {
return err
}

// plugin to unload
var pDetails []string
Expand Down Expand Up @@ -196,3 +203,34 @@ func listPlugins(ctx *cli.Context) error {

return nil
}

// storeTLSPaths extracts paths related to TLS (certificate, key) from command
// line context into temporary files. Those files are appended to list of paths
// returned from this function.
func storeTLSPaths(ctx *cli.Context, paths []string) ([]string, error) {
pCert := ctx.String("plugin-cert")
pKey := ctx.String("plugin-key")
if pCert != "" {
tmpFile, err := ioutil.TempFile("", "crt.")
if err != nil {
return paths, fmt.Errorf("Error processing plugin certificate - unable to create link:\n%v", err.Error())
}
_, err = tmpFile.WriteString(pCert)
if err != nil {
return paths, fmt.Errorf("Error processing plugin certificate - unable to write link:\n%v", err.Error())
}
paths = append(paths, tmpFile.Name())
}
if pKey != "" {
tmpFile, err := ioutil.TempFile("", "key.")
if err != nil {
return paths, fmt.Errorf("Error processing plugin key - unable to create link:\n%v", err.Error())
}
_, err = tmpFile.WriteString(pKey)
if err != nil {
return paths, fmt.Errorf("Error processing plugin key - unable to write link:\n%v", err.Error())
}
paths = append(paths, tmpFile.Name())
}
return paths, nil
}
23 changes: 15 additions & 8 deletions control/available_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ const (
)

var (
ErrPoolNotFound = errors.New("plugin pool not found")
ErrBadKey = errors.New("bad key")
ErrPoolNotFound = errors.New("plugin pool not found")
ErrBadKey = errors.New("bad key")
ErrMsgInsecurePlugin = "secure framework can't connect to insecure plugin"
ErrMsgInsecureClient = "insecure framework can't connect to secure plugin"
)

// availablePlugin represents a plugin which is
Expand All @@ -78,7 +80,13 @@ type availablePlugin struct {

// newAvailablePlugin returns an availablePlugin with information from a
// plugin.Response
func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executablePlugin) (*availablePlugin, error) {
func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executablePlugin, security client.GRPCSecurity) (*availablePlugin, error) {
if security.TLSEnabled && !resp.Meta.TLSEnabled {
return nil, errors.New(ErrMsgInsecurePlugin + "; plugin_name: " + resp.Meta.Name)
}
if !security.TLSEnabled && resp.Meta.TLSEnabled {
return nil, errors.New(ErrMsgInsecureClient + "; plugin_name: " + resp.Meta.Name)
}
if resp.Type != plugin.CollectorPluginType && resp.Type != plugin.ProcessorPluginType && resp.Type != plugin.PublisherPluginType {
return nil, strategy.ErrBadType
}
Expand Down Expand Up @@ -111,7 +119,7 @@ func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executab
}
ap.client = c
case plugin.GRPC:
c, e := client.NewCollectorGrpcClient(resp.ListenAddress, DefaultClientTimeout, resp.PublicKey, !resp.Meta.Unsecure)
c, e := client.NewCollectorGrpcClient(resp.ListenAddress, DefaultClientTimeout, security)
if e != nil {
return nil, errors.New("error while creating client connection: " + e.Error())
}
Expand All @@ -120,8 +128,7 @@ func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executab
c, e := client.NewStreamCollectorGrpcClient(
resp.ListenAddress,
DefaultClientTimeout,
resp.PublicKey,
!resp.Meta.Unsecure)
security)
if e != nil {
return nil, errors.New("error while creating client connection: " + e.Error())
}
Expand All @@ -138,7 +145,7 @@ func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executab
}
ap.client = c
case plugin.GRPC:
c, e := client.NewPublisherGrpcClient(resp.ListenAddress, DefaultClientTimeout, resp.PublicKey, !resp.Meta.Unsecure)
c, e := client.NewPublisherGrpcClient(resp.ListenAddress, DefaultClientTimeout, security)
if e != nil {
return nil, errors.New("error while creating client connection: " + e.Error())
}
Expand All @@ -155,7 +162,7 @@ func newAvailablePlugin(resp plugin.Response, emitter gomit.Emitter, ep executab
}
ap.client = c
case plugin.GRPC:
c, e := client.NewProcessorGrpcClient(resp.ListenAddress, DefaultClientTimeout, resp.PublicKey, !resp.Meta.Unsecure)
c, e := client.NewProcessorGrpcClient(resp.ListenAddress, DefaultClientTimeout, security)
if e != nil {
return nil, errors.New("error while creating client connection: " + e.Error())
}
Expand Down
5 changes: 3 additions & 2 deletions control/available_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/intelsdi-x/snap/control/fixtures"
"github.com/intelsdi-x/snap/control/plugin"
"github.com/intelsdi-x/snap/control/plugin/client"
"github.com/intelsdi-x/snap/core"
. "github.com/smartystreets/goconvey/convey"
)
Expand All @@ -45,7 +46,7 @@ func TestAvailablePlugin(t *testing.T) {
Type: plugin.CollectorPluginType,
ListenAddress: "127.0.0.1:4000",
}
ap, err := newAvailablePlugin(resp, nil, nil)
ap, err := newAvailablePlugin(resp, nil, nil, client.SecurityTLSOff())
So(ap, ShouldHaveSameTypeAs, new(availablePlugin))
So(err, ShouldBeNil)
})
Expand Down Expand Up @@ -112,7 +113,7 @@ func TestAvailablePlugins(t *testing.T) {
Type: plugin.CollectorPluginType,
ListenAddress: "localhost:asdf",
}
ap, err := newAvailablePlugin(resp, nil, nil)
ap, err := newAvailablePlugin(resp, nil, nil, client.SecurityTLSOff())
So(ap, ShouldBeNil)
So(err, ShouldNotBeNil)
})
Expand Down
16 changes: 16 additions & 0 deletions control/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ type Config struct {
Pprof bool `json:"pprof"yaml:"pprof"`
MaxPluginRestarts int `json:"max_plugin_restarts"yaml:"max_plugin_restarts"`
TempDirPath string `json:"temp_dir_path"yaml:"temp_dir_path"`
TLSCertPath string `json:"tls_cert_path"yaml:"tls_cert_path"`
TLSKeyPath string `json:"tls_key_path"yaml:"tls_key_path"`
}

const (
Expand Down Expand Up @@ -140,6 +142,12 @@ const (
},
"max_plugin_restarts": {
"type": "integer"
},
"tls_cert_path": {
"type": "string"
},
"tls_key_path": {
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -232,6 +240,14 @@ func (p *Config) GetPluginConfigDataNodeAll() cdata.ConfigDataNode {
return *p.Plugins.All
}

// IsTLSEnabled returns true if config values enable TLS security
func (p *Config) IsTLSEnabled() bool {
if p.TLSCertPath != "" && p.TLSKeyPath != "" {
return true
}
return false
}

// UnmarshalJSON unmarshals valid json into pluginConfig. An example Config
// github.com/intelsdi-x/snap/blob/master/examples/configs/snap-config-sample.json
func (p *pluginConfig) UnmarshalJSON(data []byte) error {
Expand Down
18 changes: 16 additions & 2 deletions control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,14 @@ func New(cfg *Config) *pluginControl {
}).Debug("metric catalog created")

// Plugin Manager
c.pluginManager = newPluginManager(OptSetPprof(cfg.Pprof), OptSetTempDirPath(cfg.TempDirPath))
managerOpts := []pluginManagerOpt{
OptSetPprof(cfg.Pprof),
OptSetTempDirPath(cfg.TempDirPath),
}
if cfg.IsTLSEnabled() {
managerOpts = append(managerOpts, OptEnableManagerTLS(cfg.TLSCertPath, cfg.TLSKeyPath))
}
c.pluginManager = newPluginManager(managerOpts...)
controlLogger.WithFields(log.Fields{
"_block": "new",
}).Debug("plugin manager created")
Expand All @@ -232,7 +239,11 @@ func New(cfg *Config) *pluginControl {
}).Debug("signing manager created")

// Plugin Runner
c.pluginRunner = newRunner()
if cfg.IsTLSEnabled() {
c.pluginRunner = newRunner(OptEnableRunnerTLS(cfg.TLSCertPath, cfg.TLSKeyPath))
} else {
c.pluginRunner = newRunner()
}
controlLogger.WithFields(log.Fields{
"_block": "new",
}).Debug("runner created")
Expand Down Expand Up @@ -583,6 +594,9 @@ func (p *pluginControl) returnPluginDetails(rp *core.RequestedPlugin) (*pluginDe
details.Path = rp.Path()
details.CheckSum = rp.CheckSum()
details.Signature = rp.Signature()
details.CertPath = rp.CertPath()
details.KeyPath = rp.KeyPath()
details.TLSEnabled = rp.TLSEnabled()

if filepath.Ext(rp.Path()) == ".aci" {
f, err := os.Open(rp.Path())
Expand Down
11 changes: 10 additions & 1 deletion control/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ var (
EnvVar: "SNAP_CACHE_EXPIRATION",
}

flTLSCert = cli.StringFlag{
Name: "tls-cert",
Usage: "A path to PEM-encoded certificate to use for TLS channels",
}
flTLSKey = cli.StringFlag{
Name: "tls-key",
Usage: "A path to PEM-encoded private key file to use for TLS channels",
}

flControlRpcPort = cli.StringFlag{
Name: "control-listen-port",
Usage: fmt.Sprintf("Listen port for control RPC server (default: %v)", defaultListenPort),
Expand All @@ -76,5 +85,5 @@ var (
EnvVar: "SNAP_TEMP_DIR_PATH",
}

Flags = []cli.Flag{flNumberOfPLs, flPluginLoadTimeout, flAutoDiscover, flPluginTrust, flKeyringPaths, flCache, flControlRpcPort, flControlRpcAddr, flTempDirPath}
Flags = []cli.Flag{flNumberOfPLs, flPluginLoadTimeout, flAutoDiscover, flPluginTrust, flKeyringPaths, flCache, flControlRpcPort, flControlRpcAddr, flTempDirPath, flTLSCert, flTLSKey}
)
Loading

0 comments on commit 6d6b411

Please sign in to comment.