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>
This commit is contained in:
Ryan McKinley
2020-04-14 06:02:39 -07:00
committed by GitHub
parent 61460ea3a2
commit aaab636bf4
34 changed files with 8047 additions and 70 deletions

View File

@@ -1,12 +1,19 @@
package plugins
import (
"bytes"
"crypto/sha256"
"fmt"
"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:
@@ -15,21 +22,28 @@ var publicKeyText = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
xjMEXo5V+RYJKwYBBAHaRw8BAQdAxIzDC0767A5eOHESiU8ACz5c9BWIrkbJ
/5a4m/zsFWnNG0pvbiBTbWl0aCA8am9uQGV4YW1wbGUuY29tPsJ4BBAWCgAg
BQJejlX5BgsJBwgDAgQVCAoCBBYCAQACGQECGwMCHgEACgkQ1uNw7xqtn452
hQD+LK/+1k5vdVVQDxRDyjN3+6Wiy/jK2wwH1JtHdnTUKKsA/iot3glN57wb
gaIQgQSZaE5E9tsIhGYhhNi8R743Oh4GzjgEXo5V+RIKKwYBBAGXVQEFAQEH
QCmdY+K50okUPp1NCFJxdje+Icr859fTwwRy9+hq+vUIAwEIB8JhBBgWCAAJ
BQJejlX5AhsMAAoJENbjcO8arZ+OpMwBAIcGCY1jMPo64h9G4MmFyPjL+wxn
U2YVAvfHQZnN+gD3AP47klt0/0tmSlbNwEvimZxA3tpUfNrtUO1K4E8VxSIn
Dg==
=PA1c
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 {
// pluginManifest holds details for the file manifest
type pluginManifest struct {
Plugin string `json:"plugin"`
Version string `json:"version"`
KeyID string `json:"keyId"`
@@ -39,36 +53,31 @@ type PluginManifest struct {
// 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) {
fmt.Printf("TODO... verify: %s", publicKeyText)
// block, _ := clearsign.Decode(body)
// if block == nil {
// return nil, fmt.Errorf("unable to decode manifest")
// }
func readPluginManifest(body []byte) (*pluginManifest, error) {
block, _ := clearsign.Decode(body)
if block == nil {
return nil, errors.New("unable to decode manifest")
}
// txt := string(block.Plaintext)
// fmt.Printf("PLAINTEXT: %s", txt)
// 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)
}
// // Convert to a well typed object
// manifest := &PluginManifest{}
// err := json.Unmarshal(block.Plaintext, &manifest)
// if err != nil {
// return nil, fmt.Errorf("Error parsing manifest JSON: %s", err)
// }
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(publicKeyText))
if err != nil {
return nil, errutil.Wrap("failed to parse public key", err)
}
// keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(publicKeyText))
// if err != nil {
// return nil, fmt.Errorf("failed to parse public key: %s", err)
// }
if _, err := openpgp.CheckDetachedSignature(keyring,
bytes.NewBuffer(block.Bytes),
block.ArmoredSignature.Body); err != nil {
return nil, errutil.Wrap("failed to check signature", err)
}
// if _, err := openpgp.CheckDetachedSignature(keyring,
// bytes.NewBuffer(block.Bytes),
// block.ArmoredSignature.Body); err != nil {
// return nil, fmt.Errorf("failed to check signature: %s", err)
// }
// return manifest, nil
return nil, fmt.Errorf("not yet parsing the manifest")
return manifest, nil
}
// GetPluginSignatureState returns the signature state for a plugin

View File

@@ -1,53 +1,56 @@
package plugins
import (
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
)
func TestManifestParsing(t *testing.T) {
Convey("Should validate manifest", t, func() {
txt := `
-----BEGIN PGP SIGNED MESSAGE-----
txt := `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"plugin": "grafana-googlesheets-datasource",
"version": "1.0.0-dev",
"files": {
"LICENSE": "7df059597099bb7dcf25d2a9aedfaf4465f72d8d",
"README.md": "4ebed28a02dc029719296aa847bffcea8eb5b9ff",
"gfx_sheets_darwin_amd64": "4493f107eb175b085f020c1afea04614232dc0fd",
"gfx_sheets_linux_amd64": "d8b05884e3829d1389a9c0e4b79b0aba8c19ca4a",
"gfx_sheets_windows_amd64.exe": "88f33db20182e17c72c2823fe3bed87d8c45b0fd",
"img/config-page.png": "e6d8f6704dbe85d5f032d4e8ba44ebc5d4a68c43",
"img/dashboard.png": "63d79d0e0f9db21ea168324bd4e180d6892b9d2b",
"img/graph.png": "7ea6295954b24be55b27320af2074852fb088fa1",
"img/query-editor.png": "262f2bfddb004c7ce567042e8096f9e033c9b1bd",
"img/sheets.svg": "f134ab85caff88b59ea903c5491c6a08c221622f",
"module.js": "40b8c38cea260caed3cdc01d6e3c1eca483ab5c1",
"plugin.json": "bfcae42976f0feca58eed3636655bce51702d3ed"
"README.md": "08ec6d704b6115bef57710f6d7e866c050cb50ee",
"gfx_sheets_darwin_amd64": "1b8ae92c6e80e502bb0bf2d0ae9d7223805993ab",
"gfx_sheets_linux_amd64": "f39e0cc7344d3186b1052e6d356eecaf54d75b49",
"gfx_sheets_windows_amd64.exe": "c8825dfec512c1c235244f7998ee95182f9968de",
"module.js": "aaec6f51a995b7b843b843cd14041925274d960d",
"module.js.LICENSE.txt": "7f822fe9341af8f82ad1b0c69aba957822a377cf",
"module.js.map": "c5a524f5c4237f6ed6a016d43cd46938efeadb45",
"plugin.json": "55556b845e91935cc48fae3aa67baf0f22694c3f"
},
"plugin": "grafana-googlesheets-datasource",
"version": "1.2.3",
"keyId": "ABC",
"time": 1586404562862
"time": 1586817677115,
"keyId": "7e4d0c6a708866e7"
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wl4EARYKAAYFAl6OnNMACgkQ1uNw7xqtn45r0QEAqmoB/Q5NsJZNxnM69m2A
eQhcWNyo7yxO/4NZhVvBiJkA/iXUtptWbba3aw9TSZLn95LaUjKf4YUov29r
qX6kODEP
=YjQO
-----END PGP SIGNATURE-----
`
wqEEARMKAAYFAl6U6o0ACgkQfk0ManCIZuevWAIHSvcxOy1SvvL5gC+HpYyG
VbSsUvF2FsCoXUCTQflK6VdJfSPNzm8YdCdx7gNrBdly6HEs06ZaRp44F/ve
NR7DnB0CCQHO+4FlSPtXFTzNepoc+CytQyDAeOLMLmf2Tqhk2YShk+G/YlVX
74uuP5UXZxwK2YKJovdSknDIU7MhfuvvQIP/og==
=hBea
-----END PGP SIGNATURE-----`
t.Run("valid manifest", func(t *testing.T) {
manifest, err := readPluginManifest([]byte(txt))
// Always an error for now
So(err, ShouldNotBeNil)
So(manifest, ShouldBeNil)
assert.Nil(t, err)
assert.NotNil(t, manifest)
assert.Equal(t, manifest.Plugin, "grafana-googlesheets-datasource")
})
t.Run("invalid manifest", func(t *testing.T) {
modified := strings.ReplaceAll(txt, "README.md", "xxxxxxxxxx")
manifest, err := readPluginManifest([]byte(modified))
assert.NotNil(t, err)
assert.Nil(t, manifest)
})
}