grafana/pkg/plugins/manifest.go
Ryan McKinley aaab636bf4
Plugins: verify manifest signature (#23519)
* try decode

* vendor crypto deps

* commited missing vendor deps

* Theme: Refactoring theme colors variables  (#23513)

* Theme: Typography updates

* Updated

* Updated snapshot

* Renamed colors to palette

* Introduce colors namespace

* Massive theme color move

* Removing color selection logic with more abstract concepts

* Updates

* Minor sidemenu change

* Fix example jaeger agent port in docs (#23514)

* @grafana/ui: Replace various icons using Icon component (#23442)

* Replace icons in dashboard and settings

* Replace icons in alerting

* Update batch of icons

* Implement icons accross various files

* Style updates

* Search: Fix recent and starred icons

* Update styling and details

* Replace new icon created by unicons

* Fix e2e test, styling

* Minor styling updates

Co-authored-by: Clarity-89 <homes89@ukr.net>

* trying with p512 key

* trying with p512 key

* lint

* update with real signatures

* fixes spacing in test files

* remove convey from test

* use errutil to wrap errors

* removes print statement

* splitt tests into two run statements

* unexport plugin manifest struct

Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Vitaly Zhuravlev <v-zhuravlev@users.noreply.github.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Clarity-89 <homes89@ukr.net>
2020-04-14 06:02:39 -07:00

124 lines
3.6 KiB
Go

package plugins
import (
"bytes"
"crypto/sha256"
"encoding/json"
"errors"
"io"
"io/ioutil"
"os"
"path"
"github.com/grafana/grafana/pkg/util/errutil"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/clearsign"
)
// Soon we can fetch keys from:
// https://grafana.com/api/plugins/ci/keys
var publicKeyText = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
xpMEXpTXXxMFK4EEACMEIwQBiOUQhvGbDLvndE0fEXaR0908wXzPGFpf0P0Z
HJ06tsq+0higIYHp7WTNJVEZtcwoYLcPRGaa9OQqbUU63BEyZdgAkPTz3RFd
5+TkDWZizDcaVFhzbDd500yTwexrpIrdInwC/jrgs7Zy/15h8KA59XXUkdmT
YB6TR+OA9RKME+dCJozNGUdyYWZhbmEgPGVuZ0BncmFmYW5hLmNvbT7CvAQQ
EwoAIAUCXpTXXwYLCQcIAwIEFQgKAgQWAgEAAhkBAhsDAh4BAAoJEH5NDGpw
iGbnaWoCCQGQ3SQnCkRWrG6XrMkXOKfDTX2ow9fuoErN46BeKmLM4f1EkDZQ
Tpq3SE8+My8B5BIH3SOcBeKzi3S57JHGBdFA+wIJAYWMrJNIvw8GeXne+oUo
NzzACdvfqXAZEp/HFMQhCKfEoWGJE8d2YmwY2+3GufVRTI5lQnZOHLE8L/Vc
1S5MXESjzpcEXpTXXxIFK4EEACMEIwQBtHX/SD5Qm3v4V92qpaIZQgtTX0sT
cFPjYWAHqsQ1iENrYN/vg1wU3ADlYATvydOQYvkTyT/tbDvx2Fse8PL84MQA
YKKQ6AJ3gLVvmeouZdU03YoV4MYaT8KbnJUkZQZkqdz2riOlySNI9CG3oYmv
omjUAtzgAgnCcurfGLZkkMxlmY8DAQoJwqQEGBMKAAkFAl6U118CGwwACgkQ
fk0ManCIZuc0jAIJAVw2xdLr4ZQqPUhubrUyFcqlWoW8dQoQagwO8s8ubmby
KuLA9FWJkfuuRQr+O9gHkDVCez3aism7zmJBqIOi38aNAgjJ3bo6leSS2jR/
x5NqiKVi83tiXDPncDQYPymOnMhW0l7CVA7wj75HrFvvlRI/4MArlbsZ2tBn
N1c5v9v/4h6qeA==
=DNbR
-----END PGP PUBLIC KEY BLOCK-----
`
// pluginManifest holds details for the file manifest
type pluginManifest struct {
Plugin string `json:"plugin"`
Version string `json:"version"`
KeyID string `json:"keyId"`
Time int64 `json:"time"`
Files map[string]string `json:"files"`
}
// readPluginManifest attempts to read and verify the plugin manifest
// if any error occurs or the manifest is not valid, this will return an error
func readPluginManifest(body []byte) (*pluginManifest, error) {
block, _ := clearsign.Decode(body)
if block == nil {
return nil, errors.New("unable to decode manifest")
}
// Convert to a well typed object
manifest := &pluginManifest{}
err := json.Unmarshal(block.Plaintext, &manifest)
if err != nil {
return nil, errutil.Wrap("Error parsing manifest JSON", err)
}
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(publicKeyText))
if err != nil {
return nil, errutil.Wrap("failed to parse public key", err)
}
if _, err := openpgp.CheckDetachedSignature(keyring,
bytes.NewBuffer(block.Bytes),
block.ArmoredSignature.Body); err != nil {
return nil, errutil.Wrap("failed to check signature", err)
}
return manifest, nil
}
// GetPluginSignatureState returns the signature state for a plugin
func GetPluginSignatureState(plugin *PluginBase) PluginSignature {
manifestPath := path.Join(plugin.PluginDir, "MANIFEST.txt")
byteValue, err := ioutil.ReadFile(manifestPath)
if err != nil || len(byteValue) < 10 {
return PluginSignatureUnsigned
}
manifest, err := readPluginManifest(byteValue)
if err != nil {
return PluginSignatureInvalid
}
// Make sure the versions all match
if manifest.Plugin != plugin.Id || manifest.Version != plugin.Info.Version {
return PluginSignatureModified
}
// Verify the manifest contents
for p, hash := range manifest.Files {
// Open the file
f, err := os.Open(path.Join(plugin.PluginDir, p))
if err != nil {
return PluginSignatureModified
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return PluginSignatureModified
}
sum := string(h.Sum(nil))
if sum != hash {
return PluginSignatureModified
}
}
// Everything OK
return PluginSignatureValid
}