mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 12:02:30 +08:00
Plugins: Track plugin signing errors and expose them to the frontend (#28219)
* first pass * return list * types and cleanup * add to plugin page and add styles * update comment * update comment * fix component path * simplify error component * simplify error struct * fix tests * don't export and fix string() * update naming * remove frontend * introduce phantom loader * track single error * remove error from base * remove unused struct * remove unnecessary filter * add errors endpoint * Update set log to use id field Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * skip adding BE plugins * remove errs from plugin + ds list * remove unnecessary fields * add signature state to panels * remove unused code * apply PR feedback * update comment * merge dto with model Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
@ -36,6 +36,8 @@ var (
|
||||
GrafanaLatestVersion string
|
||||
GrafanaHasUpdate bool
|
||||
plog log.Logger
|
||||
|
||||
pluginScanningErrors map[string]*PluginError
|
||||
)
|
||||
|
||||
type PluginScanner struct {
|
||||
@ -75,6 +77,7 @@ func (pm *PluginManager) Init() error {
|
||||
"renderer": RendererPlugin{},
|
||||
"transform": TransformPlugin{},
|
||||
}
|
||||
pluginScanningErrors = map[string]*PluginError{}
|
||||
|
||||
pm.log.Info("Starting plugin search")
|
||||
|
||||
@ -228,9 +231,11 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
}
|
||||
|
||||
pm.log.Debug("Found plugin", "id", plugin.Id, "signature", plugin.Signature, "hasRoot", plugin.Root != nil)
|
||||
if !scanner.validateSignature(plugin) {
|
||||
pm.log.Debug("Not adding plugin since it lacks a valid signature", "id", plugin.Id,
|
||||
"signature", plugin.Signature)
|
||||
signingError := scanner.validateSignature(plugin)
|
||||
if signingError != nil {
|
||||
pm.log.Debug("Failed to validate plugin signature. Will skip loading", "id", plugin.Id,
|
||||
"signature", plugin.Signature, "status", signingError.ErrorCode)
|
||||
pluginScanningErrors[plugin.Id] = signingError
|
||||
continue
|
||||
}
|
||||
|
||||
@ -241,8 +246,6 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
return fmt.Errorf("unknown plugin type %q", plugin.Type)
|
||||
}
|
||||
|
||||
loader := reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
|
||||
|
||||
jsonFPath := filepath.Join(plugin.PluginDir, "plugin.json")
|
||||
|
||||
// External plugins need a module.js file for SystemJS to load
|
||||
@ -268,6 +271,8 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
|
||||
loader := reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
|
||||
|
||||
// Load the full plugin, and add it to manager
|
||||
if err := loader.Load(jsonParser, plugin, scanner.backendPluginManager); err != nil {
|
||||
if errors.Is(err, duplicatePluginError{}) {
|
||||
@ -275,10 +280,8 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
scanner.errors = append(scanner.errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
pm.log.Debug("Successfully added plugin", "id", plugin.Id)
|
||||
}
|
||||
|
||||
@ -369,16 +372,16 @@ func (scanner *PluginScanner) IsBackendOnlyPlugin(pluginType string) bool {
|
||||
}
|
||||
|
||||
// validateSignature validates a plugin's signature.
|
||||
func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
|
||||
func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
|
||||
// For the time being, we choose to only require back-end plugins to be signed
|
||||
// NOTE: the state is calculated again when setting metadata on the object
|
||||
if !plugin.Backend || !s.requireSigned {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
if plugin.Signature == PluginSignatureValid {
|
||||
s.log.Debug("Plugin has valid signature", "id", plugin.Id)
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
if plugin.Root != nil {
|
||||
@ -392,7 +395,7 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
|
||||
plugin.Signature = plugin.Root.Signature
|
||||
if plugin.Signature == PluginSignatureValid {
|
||||
s.log.Debug("Plugin has valid signature (inherited from root)", "id", plugin.Id)
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -412,25 +415,42 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
|
||||
if setting.Env != setting.Dev && !allowUnsigned {
|
||||
s.log.Debug("Plugin is unsigned", "id", plugin.Id)
|
||||
s.errors = append(s.errors, fmt.Errorf("plugin %q is unsigned", plugin.Id))
|
||||
return false
|
||||
return &PluginError{
|
||||
ErrorCode: signatureMissing,
|
||||
}
|
||||
}
|
||||
|
||||
s.log.Warn("Running an unsigned backend plugin", "pluginID", plugin.Id, "pluginDir",
|
||||
plugin.PluginDir)
|
||||
return true
|
||||
return nil
|
||||
case PluginSignatureInvalid:
|
||||
s.log.Debug("Plugin %q has an invalid signature", plugin.Id)
|
||||
s.errors = append(s.errors, fmt.Errorf("plugin %q has an invalid signature", plugin.Id))
|
||||
return false
|
||||
return &PluginError{
|
||||
ErrorCode: signatureInvalid,
|
||||
}
|
||||
case PluginSignatureModified:
|
||||
s.log.Debug("Plugin %q has a modified signature", plugin.Id)
|
||||
s.errors = append(s.errors, fmt.Errorf("plugin %q's signature has been modified", plugin.Id))
|
||||
return false
|
||||
return &PluginError{
|
||||
ErrorCode: signatureModified,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Plugin %q has unrecognized plugin signature state %q", plugin.Id, plugin.Signature))
|
||||
}
|
||||
}
|
||||
|
||||
func ScanningErrors() []PluginError {
|
||||
scanningErrs := make([]PluginError, 0)
|
||||
for id, e := range pluginScanningErrors {
|
||||
scanningErrs = append(scanningErrs, PluginError{
|
||||
ErrorCode: e.ErrorCode,
|
||||
PluginID: id,
|
||||
})
|
||||
}
|
||||
return scanningErrs
|
||||
}
|
||||
|
||||
func GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
|
||||
plug, exists := Plugins[pluginId]
|
||||
if !exists {
|
||||
|
Reference in New Issue
Block a user