From 6f7ef3612fc49bc7a8a7c08b8420605c3eb02866 Mon Sep 17 00:00:00 2001 From: Jordan May Date: Wed, 4 May 2022 16:09:15 -0400 Subject: [PATCH 1/2] remove unused go-jose crypter --- go-jose/crypter.go | 271 --------------------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 go-jose/crypter.go diff --git a/go-jose/crypter.go b/go-jose/crypter.go deleted file mode 100644 index f484877..0000000 --- a/go-jose/crypter.go +++ /dev/null @@ -1,271 +0,0 @@ -package crypter - -import ( - "bytes" - "crypto" - "fmt" - "strings" - - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/base64" - "encoding/binary" - "encoding/json" - "io/ioutil" - "math/big" - "net/http" - - "github.com/goware/urlx" - "github.com/lainio/err2" - "github.com/lainio/err2/try" - - jose "github.com/flatheadmill/go-jose/v3" - jcipher "github.com/flatheadmill/go-jose/v3/cipher" -) - -func encode64(buffer []byte) string { - return base64.RawURLEncoding.EncodeToString(buffer) -} - -func base64Decode(encoded string) ([]byte, error) { - return base64.RawURLEncoding.DecodeString(encoded) -} - -type jsonTang struct { - Location string `json:"url"` - Advertisement jose.JSONWebKeySet `json:"adv"` -} - -type jsonClevis struct { - Plugin string `json:"pin"` - Tang jsonTang `json:"tang"` -} - -type Crypter struct { - recipient jose.Recipient - crypter *Crypter2 - encrypterOptions jose.EncrypterOptions -} - -func NewCrypter(url string, fingerprint string) (crypter Crypter, err error) { - defer err2.Return(&err) - - try.To1(base64Decode(fingerprint)) - - url = try.To1(urlx.Normalize(try.To1(urlx.Parse(strings.TrimSuffix(url, "/"))))) - advGet := try.To1(http.Get(fmt.Sprintf("%s/adv/%s", url, fingerprint))) - defer advGet.Body.Close() - - advJSON := try.To1(ioutil.ReadAll(advGet.Body)) - - crypter2 := try.To1(NewCrypter2(advJSON, fingerprint)) - - var adv map[string]interface{} - err2.Check(json.Unmarshal(advJSON, &adv)) - - payload, err := base64.RawURLEncoding.DecodeString(adv["payload"].(string)) - err2.Check(err) - - // TODO Missing verification. Just want to get it into an object for now. - - var keySet jose.JSONWebKeySet - err = json.Unmarshal(payload, &keySet) - err2.Check(err) - - var deriver *jose.JSONWebKey -Keys: - for _, key := range keySet.Keys { - for _, op := range key.KeyOps { - if op == "deriveKey" { - deriver = &key - break Keys - } - } - } - deriver.Algorithm = "" - deriver.KeyOps = nil - - extraHeaders := make(map[jose.HeaderKey]interface{}) - - extraHeaders["clevis"] = jsonClevis{ - Plugin: "tang", - Tang: jsonTang{ - Location: url, - Advertisement: keySet, - }, - } - - thumbprint, err := deriver.Thumbprint(crypto.SHA256) - err2.Check(err) - extraHeaders["kid"] = base64.RawURLEncoding.EncodeToString(thumbprint) - - return Crypter{ - encrypterOptions: jose.EncrypterOptions{ExtraHeaders: extraHeaders}, - recipient: jose.Recipient{Algorithm: jose.ECDH_ES, Key: deriver.Key}, - crypter: crypter2, - }, nil -} - -func (c *Crypter) Encrypt(plain []byte) (compact string, err error) { - defer err2.Return(&err) - encrypter := try.To1(jose.NewEncrypter(jose.A256GCM, c.recipient, &c.encrypterOptions)) - cipher := try.To1(encrypter.Encrypt(plain)) - compact = try.To1(cipher.CompactSerialize()) - return compact, nil -} - -func dSize(curve elliptic.Curve) int { - order := curve.Params().P - bitLen := order.BitLen() - size := bitLen / 8 - if bitLen%8 != 0 { - size++ - } - return size -} - -func lengthPrefixed(data []byte) []byte { - out := make([]byte, len(data)+4) - binary.BigEndian.PutUint32(out, uint32(len(data))) - copy(out[4:], data) - return out -} - -// DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA. -// It is an error to call this function with a private/public key that are not on the same -// curve. Callers must ensure that the keys are valid before calling this function. Output -// size may be at most 1<<16 bytes (64 KiB). -func DeriveECDHES(alg string, apuData, apvData []byte, pub *ecdsa.PublicKey, size int) []byte { - if size > 1<<16 { - panic("ECDH-ES output size too large, must be less than or equal to 1<<16") - } - - // algId, partyUInfo, partyVInfo inputs must be prefixed with the length - algID := lengthPrefixed([]byte(alg)) - ptyUInfo := lengthPrefixed(apuData) - ptyVInfo := lengthPrefixed(apvData) - - // suppPubInfo is the encoded length of the output size in bits - supPubInfo := make([]byte, 4) - binary.BigEndian.PutUint32(supPubInfo, uint32(size)*8) - - zBytes := pub.X.Bytes() - - // Note that calling z.Bytes() on a big.Int may strip leading zero bytes from - // the returned byte array. This can lead to a problem where zBytes will be - // shorter than expected which breaks the key derivation. Therefore we must pad - // to the full length of the expected coordinate here before calling the KDF. - octSize := dSize(pub.Curve) - if len(zBytes) != octSize { - zBytes = append(bytes.Repeat([]byte{0}, octSize-len(zBytes)), zBytes...) - } - - reader := jcipher.NewConcatKDF(crypto.SHA256, zBytes, algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{}) - key := make([]byte, size) - - // Read on the KDF will never fail - _, _ = reader.Read(key) - - return key -} - -type jsonProtected struct { - KeyIdentifier string `json:"kid"` - Algorithm string `json:"alg"` - Encryption string `json:"enc"` - Clevis jsonClevis `json:"clevis"` - EphemeralPublicKey jose.JSONWebKey `json:"epk"` -} - -func Decrypt(jwe []byte) ([]byte, error) { - // Had a go at using jose.ParseEncryption but it returns the `ExtraHeaders` - // as a tree of interfaces so you have to conert them either by serializing - // and deserialing the JSON or using something like `mapstructure`. - // - // https://github.com/mitchellh/mapstructure - - // - dot := bytes.IndexByte(jwe, byte('.')) - header, err := base64.RawURLEncoding.DecodeString(string(jwe[0:dot])) - protected := jsonProtected{} - - err = json.Unmarshal(header, &protected) - err2.Check(err) - - var remote *ecdsa.PublicKey - for _, key := range protected.Clevis.Tang.Advertisement.Keys { - thumbprint, err := key.Thumbprint(crypto.SHA256) - if err != nil { - panic(err) - } - if protected.KeyIdentifier == base64.RawURLEncoding.EncodeToString(thumbprint) { - remote = key.Key.(*ecdsa.PublicKey) - break - } - } - - ephemeral, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - err2.Check(err) - - client := protected.EphemeralPublicKey.Key.(*ecdsa.PublicKey) - x, y := elliptic.P521().Add(client.X, client.Y, ephemeral.X, ephemeral.Y) - ecmr := jose.JSONWebKey{Key: &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}} - ecmrJSON, err := json.Marshal(&ecmr) - err2.Check(err) - - url := protected.Clevis.Tang.Location + "/rec/" + protected.KeyIdentifier - req, err := http.NewRequest("POST", url, bytes.NewBuffer(ecmrJSON)) - req.Header.Set("Content-Type", "application/jwk+json") - - agent := &http.Client{} - post, err := agent.Do(req) - err2.Check(err) - defer post.Body.Close() - - body, err := ioutil.ReadAll(post.Body) - err2.Check(err) - - var response jose.JSONWebKey - err = json.Unmarshal(body, &response) - err2.Check(err) - - // Calling big.Int.Bytes() strips leading zeros, so this could be a problem, - // but the `D` value of `spec521r1` does not have any leading zeros. - x, y = elliptic.P521().ScalarMult(remote.X, remote.Y, ephemeral.D.Bytes()) - - negY := new(big.Int) - negY.Sub(elliptic.P521().Params().P, y) - x, y = elliptic.P521().Add(response.Key.(*ecdsa.PublicKey).X, response.Key.(*ecdsa.PublicKey).Y, x, negY) - - recovered := jose.JSONWebKey{Key: &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}} - - key := DeriveECDHES(protected.Encryption, []byte{}, []byte{}, recovered.Key.(*ecdsa.PublicKey), 32) - - parts := strings.Split(string(jwe), ".") - if len(parts) != 5 { - err2.Check(fmt.Errorf("compact JWE format must have five parts")) - } - - iv, err := base64Decode(parts[2]) - err2.Check(err) - - ciphertext, err := base64Decode(parts[3]) - err2.Check(err) - - tag, err := base64Decode(parts[4]) - err2.Check(err) - - aeadCipher, err := aes.NewCipher(key) - err2.Check(err) - - aead, err := cipher.NewGCM(aeadCipher) - err2.Check(err) - - plaintext, err := aead.Open(nil, iv, append(ciphertext, tag...), []byte(parts[0])) - err2.Check(err) - - return plaintext, nil -} From 5e9f49bdc9daf5df912a447a517ebce063a6b234 Mon Sep 17 00:00:00 2001 From: Jordan May Date: Wed, 4 May 2022 16:14:15 -0400 Subject: [PATCH 2/2] added prometheus metrics client. moved health check from Crypter up to Plugin GRPC calls. added metrics to Plugin gRPC calls --- cmd/server/server.go | 11 ++-- crypter/crypter.go | 10 ++-- go.mod | 15 ++++-- go.sum | 34 ++++++++---- plugin/metrics.go | 37 +++++++++++++ plugin/plugin.go | 120 +++++++++++++++++++++++++++++++++++-------- 6 files changed, 182 insertions(+), 45 deletions(-) create mode 100644 plugin/metrics.go diff --git a/cmd/server/server.go b/cmd/server/server.go index 03a11a7..76b3b74 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -7,9 +7,11 @@ import ( "github.com/flatheadmill/tang-encryption-provider/crypter" "github.com/gorilla/mux" "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus/promhttp" "net/http" "os" "os/signal" + "runtime" "syscall" "time" @@ -36,17 +38,17 @@ const ( func main() { var spec Specification try.To(envconfig.Process("tang_kms", &spec)) - log := logger.New(os.Stdout) + log := logger.New(os.Stdout).WithFields(map[string]interface{}{"go_version": runtime.Version(), "env": spec.Env}) if spec.Env == EnvLocal { log.Console() } log.MsgWithFields(map[string]interface{}{"thumbprint": spec.Thumbprint, "unix_socket": spec.UnixSocket}, "") crypt := try.To1(crypter.NewCrypter(spec.ServerUrl, spec.Thumbprint)) + plugin := try.To1(plugin.New(log, crypt, spec.UnixSocket)) + httpSvr := setupHttpServer(log, []HealthComponent{NewHealthComponent(plugin, "tang-encryption-provider plugin")}, spec.HttpPort) - httpSvr := setupHttpServer(log, []HealthComponent{NewHealthComponent(crypt, "tang_crypter")}, spec.HttpPort) - - err := run(log, try.To1(plugin.New(log, crypt, spec.UnixSocket)), httpSvr) + err := run(log, plugin, httpSvr) if err != nil { fmt.Printf("exited with error: %T %v\n", err, err) } @@ -85,6 +87,7 @@ func setupHttpServer(l logger.Logger, components []HealthComponent, httpPort str r := mux.NewRouter() r.HandleFunc("/livez", healthAPI.Health) r.HandleFunc("/readyz", healthAPI.Health) + r.Handle("/metrics", promhttp.Handler()) return &http.Server{Addr: ":" + httpPort, Handler: r} } diff --git a/crypter/crypter.go b/crypter/crypter.go index e848f03..f1f39ce 100644 --- a/crypter/crypter.go +++ b/crypter/crypter.go @@ -114,8 +114,8 @@ func NewCrypter(url string, thumbprint string) (crypter *Crypter, err error) { } func (c *Crypter) Encrypt(plain []byte) (cipher []byte, err error) { - defer err2.Handle(&err, handler.Handler(&err)) - return try.To1(jwe.Encrypt(plain, jwa.ECDH_ES, c.exchangeKey, jwa.A256GCM, jwa.NoCompress, jwe.WithProtectedHeaders(c.headers))), nil + cipher, err = jwe.Encrypt(plain, jwa.ECDH_ES, c.exchangeKey, jwa.A256GCM, jwa.NoCompress, jwe.WithProtectedHeaders(c.headers)) + return cipher, errors.Wrap(err, "failed to encrypt plaintext locally with JWE") } func (c *Crypter) Decrypt(cipher []byte) (plain []byte, err error) { @@ -124,12 +124,12 @@ func (c *Crypter) Decrypt(cipher []byte) (plain []byte, err error) { func Decrypt(cipher []byte) (plain []byte, err error) { plain, err = clevis.Decrypt(cipher) - err = errors.Wrap(err, "failed to decrypt cipher") + err = errors.Wrap(err, "failed to decrypt cipher with clevis") return } func (c Crypter) Health() error { - randomPlaintext := RandomHex(8) + randomPlaintext := randomHex(8) cipher, err := c.Encrypt([]byte(randomPlaintext)) if err != nil { return errors.Wrap(err, "failed to encrypt random text") @@ -149,7 +149,7 @@ func init() { rand.Seed(time.Now().UnixNano()) } -func RandomHex(n int) string { +func randomHex(n int) string { if n <= 0 { return "" } diff --git a/go.mod b/go.mod index 0fb651a..aae2170 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.18 require ( github.com/anatol/clevis.go v0.0.0-20220325231436-1877edc32744 - github.com/flatheadmill/go-jose/v3 v3.0.2 github.com/gogo/protobuf v1.3.2 github.com/gorilla/mux v1.8.0 github.com/goware/urlx v0.3.1 @@ -12,8 +11,9 @@ require ( github.com/lainio/err2 v0.8.0 github.com/lestrrat-go/jwx v1.2.20 github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.12.1 github.com/rs/zerolog v1.26.1 - golang.org/x/net v0.0.0-20211209124913-491a49abca63 + golang.org/x/net v0.0.0-20220225172249-27dd8689420f google.golang.org/grpc v1.45.0 k8s.io/apiserver v0.23.5 ) @@ -21,8 +21,9 @@ require ( require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/goccy/go-json v0.9.5 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-tpm v0.3.3 // indirect @@ -31,9 +32,13 @@ require ( github.com/lestrrat-go/httpcc v1.0.0 // indirect github.com/lestrrat-go/iter v1.0.1 // indirect github.com/lestrrat-go/option v1.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.34.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect + golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/protobuf v1.28.0 // indirect ) diff --git a/go.sum b/go.sum index d71fd1c..7f79b40 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,7 @@ github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiU github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -83,6 +84,8 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -138,8 +141,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flatheadmill/go-jose/v3 v3.0.2 h1:e5CI0qsU3eRRPv8ckJvYM9BfJzjKdrEACUSwdbzu7dw= -github.com/flatheadmill/go-jose/v3 v3.0.2/go.mod h1:9Kr6zKaRHEcE0p4Gnuy3Kn8zO/SL/a5IO+2LW2K/mbw= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -150,14 +151,14 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -342,6 +343,7 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -394,9 +396,12 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -404,11 +409,16 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= +github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -514,7 +524,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -605,8 +614,10 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -621,6 +632,7 @@ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -696,10 +708,13 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -890,8 +905,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugin/metrics.go b/plugin/metrics.go new file mode 100644 index 0000000..83bb120 --- /dev/null +++ b/plugin/metrics.go @@ -0,0 +1,37 @@ +package plugin + +import "github.com/prometheus/client_golang/prometheus" + +func init() { + registerPrometheusMetrics() +} + +func registerPrometheusMetrics() { + prometheus.MustRegister(kmsOperationCounter) + prometheus.MustRegister(kmsLatencyMetric) +} + +var ( + kmsOperationCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "tang_encryption_provider_kms_operations_total", + Help: "total tang encryption provider kms operations", + }, + []string{ + "status", + "operation", + }, + ) + + kmsLatencyMetric = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "tang_encryption_provider_kms_operation_latency_ms", + Help: "Response latency in milliseconds for tang encryption provider kms operation ", + Buckets: prometheus.ExponentialBuckets(2, 2, 14), + }, + []string{ + "status", + "operation", + }, + ) +) diff --git a/plugin/plugin.go b/plugin/plugin.go index 4d0197f..4bd0877 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -2,9 +2,15 @@ package plugin import ( "context" + cryptoRand "crypto/rand" + "encoding/hex" + "fmt" + "github.com/pkg/errors" + "math/rand" "net" "os" "strings" + "time" "github.com/flatheadmill/tang-encryption-provider/crypter" "github.com/lainio/err2" @@ -19,6 +25,11 @@ const ( apiVersion = "v1beta1" runtimeName = "TangKMS" runtimeVersion = "0.0.1" + + statusSuccess = "success" + statusFailure = "failure" + operationEncrypt = "encrypt" + operationDecrypt = "decrypt" ) type Crypter interface { @@ -49,34 +60,80 @@ func New(l logger, crypter Crypter, socket string) (plugin *Plugin, err error) { return &Plugin{crypter: crypter, socket: socket, logger: l}, nil } -func (g *Plugin) Version(ctx context.Context, request *VersionRequest) (*VersionResponse, error) { +func (p *Plugin) Version(ctx context.Context, request *VersionRequest) (*VersionResponse, error) { return &VersionResponse{Version: apiVersion, RuntimeName: runtimeName, RuntimeVersion: runtimeVersion}, nil } +func (p *Plugin) Health() error { + randomPlaintext := randomHex(8) + encryptResp, err := p.Encrypt(context.Background(), &EncryptRequest{Plain: []byte(randomPlaintext)}) + if err != nil { + return errors.Wrap(err, "failed to encrypt random text") + } + + decryptResp, err := p.Decrypt(context.Background(), &DecryptRequest{Cipher: encryptResp.GetCipher()}) + if err != nil { + return errors.Wrap(err, "failed to decrypt random text cipher") + } + if randomPlaintext != string(decryptResp.GetPlain()) { + return errors.Errorf("decrypted text does not equal input random text: want: %s got: %s", randomPlaintext, decryptResp.GetPlain()) + } + return nil +} + // TODO Notify only of error and add metrics. -func (g *Plugin) Encrypt(ctx context.Context, request *EncryptRequest) (response *EncryptResponse, err error) { - defer err2.Handle(&err, handler.Handler(&err)) - cipher := try.To1(g.crypter.Encrypt(request.Plain)) - g.logger.MsgWithFields(LogFields{"jwe": string(cipher)}, "encrypted") - return &EncryptResponse{Cipher: cipher}, nil +func (p *Plugin) Encrypt(ctx context.Context, request *EncryptRequest) (response *EncryptResponse, err error) { + sTime := time.Now() + defer func() { + status := statusSuccess + if err != nil { + status = statusFailure + } + // TODO: write/use interface for metrics or + // Maybe write wrapper for plugin Encrypt/Decrypt that handles metrics/logging separately. + kmsOperationCounter.WithLabelValues(status, operationEncrypt).Inc() + kmsLatencyMetric.WithLabelValues(status, operationEncrypt).Observe(msSince(sTime)) + }() + var cipher []byte + cipher, err = p.crypter.Encrypt(request.Plain) + err = errors.Wrap(err, "failed to encrypt") + + if !p.logger.Err(err) { + p.logger.MsgWithFields(LogFields{"jwe": string(cipher)}, "encrypted") + } + + return &EncryptResponse{Cipher: cipher}, err } // TODO Notify only of error and add metrics. -func (g *Plugin) Decrypt(ctx context.Context, request *DecryptRequest) (response *DecryptResponse, err error) { - defer err2.Handle(&err, handler.Handler(&err)) - g.logger.MsgWithFields(LogFields{"jwe": string(request.Cipher)}, "decrypting") - plain := try.To1(crypter.Decrypt(request.Cipher)) - return &DecryptResponse{Plain: plain}, nil +func (p *Plugin) Decrypt(ctx context.Context, request *DecryptRequest) (response *DecryptResponse, err error) { + sTime := time.Now() + defer func() { + status := statusSuccess + if err != nil { + status = statusFailure + } + // TODO: write/use interface for metrics or + // Maybe write wrapper for plugin Encrypt/Decrypt that handles metrics/logging separately. + kmsOperationCounter.WithLabelValues(status, operationDecrypt).Inc() + kmsLatencyMetric.WithLabelValues(status, operationDecrypt).Observe(msSince(sTime)) + }() + p.logger.MsgWithFields(LogFields{"jwe": string(request.Cipher)}, "decrypting") + var plain []byte + plain, err = crypter.Decrypt(request.Cipher) + err = errors.Wrap(err, "failed to decrypt") + p.logger.Err(err) + return &DecryptResponse{Plain: plain}, err } -func (g *Plugin) setupRPCServer() (err error) { +func (p *Plugin) setupRPCServer() (err error) { defer err2.Handle(&err, handler.Handler(&err)) // @ implies the use of Linux socket namespace - no file on disk and nothing // to clean-up. - if !strings.HasPrefix(g.socket, "@") { + if !strings.HasPrefix(p.socket, "@") { try.To(func() error { - err = os.Remove(g.socket) + err = os.Remove(p.socket) if err != nil && !os.IsNotExist(err) { return err } @@ -84,19 +141,19 @@ func (g *Plugin) setupRPCServer() (err error) { }()) } - g.Listener = try.To1(net.Listen(netProtocol, g.socket)) - g.logger.Msgf("Listening on unix domain socket: %s", g.socket) + p.Listener = try.To1(net.Listen(netProtocol, p.socket)) + p.logger.Msgf("Listening on unix domain socket: %s", p.socket) - g.Server = grpc.NewServer() - RegisterKeyManagementServiceServer(g.Server, g) + p.Server = grpc.NewServer() + RegisterKeyManagementServiceServer(p.Server, p) return nil } -func (g *Plugin) ServeKMSRequests() (*grpc.Server, chan error) { +func (p *Plugin) ServeKMSRequests() (*grpc.Server, chan error) { errorChannel := make(chan error, 1) - if err := g.setupRPCServer(); err != nil { + if err := p.setupRPCServer(); err != nil { errorChannel <- err close(errorChannel) return nil, errorChannel @@ -104,8 +161,27 @@ func (g *Plugin) ServeKMSRequests() (*grpc.Server, chan error) { go func() { defer close(errorChannel) - errorChannel <- g.Serve(g.Listener) + errorChannel <- p.Serve(p.Listener) }() - return g.Server, errorChannel + return p.Server, errorChannel +} + +func msSince(startTime time.Time) float64 { + return time.Since(startTime).Seconds() * 1000 +} +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func randomHex(n int) string { + if n <= 0 { + return "" + } + buf := make([]byte, (n/2)+(n%2)) + if _, err := cryptoRand.Read(buf); err != nil { + fmt.Println(err) + return "" + } + return hex.EncodeToString(buf)[:n] }