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

Commit

Permalink
417: Verify plugins before running plugins. Always extract plugin from
Browse files Browse the repository at this point in the history
package before running plugin. Add ACI package using appc/spec for
extracting and validating packages.
  • Loading branch information
geauxvirtual committed Nov 19, 2015
1 parent ceec31c commit 39fbbe5
Show file tree
Hide file tree
Showing 26 changed files with 639 additions and 548 deletions.
4 changes: 4 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions control/available_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ package control
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -82,6 +85,9 @@ type availablePlugin struct {
failedHealthChecks int
healthChan chan error
ePlugin executablePlugin
exec string
execPath string
fromPackage bool
}

// newAvailablePlugin returns an availablePlugin with information from a
Expand Down Expand Up @@ -202,6 +208,15 @@ func (a *availablePlugin) Kill(r string) error {
"block": "kill",
"aplugin": a,
}).Info("hard killing available plugin")
if a.fromPackage {
log.WithFields(log.Fields{
"_module": "control-aplugin",
"block": "kill",
"aplugin": a,
"pluginPath": path.Join(a.execPath, a.exec),
}).Debug("deleting available plugin path")
os.RemoveAll(filepath.Dir(a.execPath))
}
return a.ePlugin.Kill()
}

Expand Down
176 changes: 127 additions & 49 deletions control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ limitations under the License.
package control

import (
"crypto/sha256"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
Expand All @@ -39,8 +43,8 @@ import (
"github.com/intelsdi-x/pulse/core/control_event"
"github.com/intelsdi-x/pulse/core/ctypes"
"github.com/intelsdi-x/pulse/core/perror"
"github.com/intelsdi-x/pulse/pkg/aci"
"github.com/intelsdi-x/pulse/pkg/psigning"
"github.com/intelsdi-x/pulse/pkg/unpackage"
)

// Plugin token = token generated by plugin and passed to control
Expand Down Expand Up @@ -91,14 +95,14 @@ type runsPlugins interface {
SetStrategy(RoutingStrategy)
Strategy() RoutingStrategy
Monitor() *monitor
runPlugin(string) error
runPlugin(*pluginDetails) error
}

type managesPlugins interface {
teardown()
get(string) (*loadedPlugin, error)
all() map[string]*loadedPlugin
LoadPlugin(string, gomit.Emitter) (*loadedPlugin, perror.PulseError)
LoadPlugin(*pluginDetails, gomit.Emitter) (*loadedPlugin, perror.PulseError)
UnloadPlugin(core.Plugin) (*loadedPlugin, perror.PulseError)
SetMetricCatalog(catalogsMetrics)
GenerateArgs(pluginPath string) plugin.Arg
Expand All @@ -119,7 +123,7 @@ type catalogsMetrics interface {
}

type managesSigning interface {
ValidateSignature(keyringFiles []string, signedFile string, signatureFile string) error
ValidateSignature([]string, string, []byte) error
}

type ControlOpt func(*pluginControl)
Expand Down Expand Up @@ -251,48 +255,17 @@ func (p *pluginControl) Stop() {
// Load is the public method to load a plugin into
// the LoadedPlugins array and issue an event when
// successful.
func (p *pluginControl) Load(files ...string) (core.CatalogedPlugin, perror.PulseError) {
func (p *pluginControl) Load(rp *core.RequestedPlugin) (core.CatalogedPlugin, perror.PulseError) {
f := map[string]interface{}{
"_block": "load",
}
if len(files) > 2 {
return nil, perror.New(errors.New("Load plugin limited to a single plugin plus signature file if plugin is signed"))
}

//Check plugin signing
var signed bool
var pluginPath, signatureFile string

pluginPath = files[0]
signatureFile = ""
if len(files) == 2 {
signatureFile = files[1]
}
switch p.pluginTrust {
case PluginTrustDisabled:
signed = false
case PluginTrustEnabled:
err := p.signingManager.ValidateSignature(p.keyringFiles, pluginPath, signatureFile)
if err != nil {
return nil, perror.New(err)
}
signed = true
case PluginTrustWarn:
if _, err := os.Stat(signatureFile); err != nil {
signed = false
controlLogger.WithFields(f).Warn("Loading unsigned plugin ", pluginPath)
} else {
err := p.signingManager.ValidateSignature(p.keyringFiles, pluginPath, signatureFile)
if err != nil {
return nil, perror.New(err)
}
signed = true
}
details, perr := p.returnPluginDetails(rp)
if perr != nil {
return nil, perr
}

path, _, err := unpackage.Unpackager(pluginPath)
if err != nil {
return nil, perror.New(err)
if details.IsPackage {
defer os.RemoveAll(filepath.Dir(details.ExecPath))
}

controlLogger.WithFields(f).Info("plugin load called")
Expand All @@ -303,24 +276,96 @@ func (p *pluginControl) Load(files ...string) (core.CatalogedPlugin, perror.Puls
return nil, pe
}

pl, pe := p.pluginManager.LoadPlugin(path, p.eventManager)
pl, pe := p.pluginManager.LoadPlugin(details, p.eventManager)
if pe != nil {
return nil, pe
}
pl.Signed = signed
pl.SignatureFile = signatureFile

// If plugin was loaded from a package, remove ExecPath for
// the temporary plugin that was used for load
if pl.Details.IsPackage {
pl.Details.ExecPath = ""
}

// defer sending event
event := &control_event.LoadPluginEvent{
Name: pl.Meta.Name,
Version: pl.Meta.Version,
Type: int(pl.Meta.Type),
Signed: pl.Signed,
Signed: pl.Details.Signed,
}
defer p.eventManager.Emit(event)
return pl, nil
}

func (p *pluginControl) verifySignature(rp *core.RequestedPlugin) (bool, perror.PulseError) {
f := map[string]interface{}{
"_block": "verifySignature",
}
switch p.pluginTrust {
case PluginTrustDisabled:
return false, nil
case PluginTrustEnabled:
err := p.signingManager.ValidateSignature(p.keyringFiles, rp.Path(), rp.Signature())
if err != nil {
return false, perror.New(err)
}
case PluginTrustWarn:
if rp.Signature() == nil {
controlLogger.WithFields(f).Warn("Loading unsigned plugin ", rp.Path())
return false, nil
} else {
err := p.signingManager.ValidateSignature(p.keyringFiles, rp.Path(), rp.Signature())
if err != nil {
return false, perror.New(err)
}
}
}
return true, nil

}

func (p *pluginControl) returnPluginDetails(rp *core.RequestedPlugin) (*pluginDetails, perror.PulseError) {
details := &pluginDetails{}
var perr perror.PulseError
//Check plugin signing
details.Signed, perr = p.verifySignature(rp)
if perr != nil {
return nil, perr
}

details.Path = rp.Path()
details.CheckSum = rp.CheckSum()
details.Signature = rp.Signature()

if filepath.Ext(rp.Path()) == ".aci" {
f, err := os.Open(rp.Path())
if err != nil {
return nil, perror.New(err)
}
defer f.Close()
if err := aci.Validate(f); err != nil {
return nil, perror.New(err)
}
tempPath, err := aci.Extract(f)
if err != nil {
return nil, perror.New(err)
}
details.ExecPath = path.Join(tempPath, "rootfs")
if details.Manifest, err = aci.Manifest(f); err != nil {
return nil, perror.New(err)
}
details.Exec = details.Manifest.App.Exec[0]
details.IsPackage = true
} else {
details.IsPackage = false
details.Exec = filepath.Base(rp.Path())
details.ExecPath = filepath.Dir(rp.Path())
}

return details, nil
}

func (p *pluginControl) Unload(pl core.Plugin) (core.CatalogedPlugin, perror.PulseError) {
up, err := p.pluginManager.UnloadPlugin(pl)
if err != nil {
Expand All @@ -336,9 +381,17 @@ func (p *pluginControl) Unload(pl core.Plugin) (core.CatalogedPlugin, perror.Pul
return up, nil
}

func (p *pluginControl) SwapPlugins(inPath string, out core.CatalogedPlugin) perror.PulseError {
func (p *pluginControl) SwapPlugins(in *core.RequestedPlugin, out core.CatalogedPlugin) perror.PulseError {

details, perr := p.returnPluginDetails(in)
if perr != nil {
return perr
}
if details.IsPackage {
defer os.RemoveAll(filepath.Dir(details.ExecPath))
}

lp, err := p.pluginManager.LoadPlugin(inPath, p.eventManager)
lp, err := p.pluginManager.LoadPlugin(details, p.eventManager)
if err != nil {
return err
}
Expand Down Expand Up @@ -552,7 +605,12 @@ func (p *pluginControl) SubscribeDeps(taskId string, mts []core.Metric, plugins
}
pool.subscribe(taskId, unboundSubscriptionType)
if pool.eligible() {
err = p.pluginRunner.runPlugin(latest.Path)
err = p.verifyPlugin(latest)
if err != nil {
perrs = append(perrs, perror.New(err))
return perrs
}
err = p.pluginRunner.runPlugin(latest.Details)
if err != nil {
perrs = append(perrs, perror.New(err))
return perrs
Expand All @@ -571,7 +629,12 @@ func (p *pluginControl) SubscribeDeps(taskId string, mts []core.Metric, plugins
perrs = append(perrs, perror.New(err))
return perrs
}
err = p.pluginRunner.runPlugin(pl.Path)
err = p.verifyPlugin(pl)
if err != nil {
perrs = append(perrs, perror.New(err))
return perrs
}
err = p.pluginRunner.runPlugin(pl.Details)
if err != nil {
perrs = append(perrs, perror.New(err))
return perrs
Expand All @@ -587,6 +650,21 @@ func (p *pluginControl) SubscribeDeps(taskId string, mts []core.Metric, plugins
return perrs
}

func (p *pluginControl) verifyPlugin(lp *loadedPlugin) error {
b, err := ioutil.ReadFile(lp.Details.Path)
if err != nil {
return err
}
cs := sha256.Sum256(b)
if lp.Details.CheckSum != cs {
return fmt.Errorf(fmt.Sprintf("Current plugin checksum (%x) does not match checksum when plugin was first loaded (%x).", cs, lp.Details.CheckSum))
}
if lp.Details.Signed {
return p.signingManager.ValidateSignature(p.keyringFiles, lp.Details.Path, lp.Details.Signature)
}
return nil
}

func (p *pluginControl) sendPluginSubscriptionEvent(taskId string, pl core.Plugin) perror.PulseError {
pt, err := core.ToPluginType(pl.TypeName())
if err != nil {
Expand Down
Loading

0 comments on commit 39fbbe5

Please sign in to comment.