From 5f719a3dce012799ed4ca09600c9fc4daa235355 Mon Sep 17 00:00:00 2001 From: Moritz Johner Date: Sat, 1 May 2021 21:49:12 +0200 Subject: [PATCH] fix(tpl): fail on parse/execute error fixes #126 --- pkg/template/template.go | 77 ++++++++++++++++++----------------- pkg/template/template_test.go | 76 +++++++++++++++++++++++++++------- 2 files changed, 101 insertions(+), 52 deletions(-) diff --git a/pkg/template/template.go b/pkg/template/template.go index 8ea93f3de..5b18d3f6b 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -18,13 +18,13 @@ import ( "encoding/base64" "encoding/json" "encoding/pem" + "fmt" "strings" tpl "text/template" "github.com/youmark/pkcs8" "golang.org/x/crypto/pkcs12" corev1 "k8s.io/api/core/v1" - ctrl "sigs.k8s.io/controller-runtime" ) var tplFuncs = tpl.FuncMap{ @@ -46,88 +46,91 @@ var tplFuncs = tpl.FuncMap{ "lower": strings.ToLower, } -var log = ctrl.Log.WithName("template") +const ( + errParse = "unable to parse template at key %s: %s" + errExecute = "unable to execute template at key %s: %s" + errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s" + errConvertPrivKey = "unable to convert pkcs12 private key: %s" + errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s" + errEncodePEMKey = "unable to encode pem private key: %s" + errEncodePEMCert = "unable to encode pem certificate: %s" + errDecodeBase64 = "unable to decode base64: %s" + errUnmarshalJSON = "unable to unmarshal json: %s" + errMarshalJSON = "unable to marshal json: %s" +) -// Execute uses an best-effort approach to render the secret data as template. +// Execute renders the secret data as template. If an error occurs processing is stopped immediately. func Execute(secret *corev1.Secret, data map[string][]byte) error { for k, v := range secret.Data { t, err := tpl.New(k). Funcs(tplFuncs). Parse(string(v)) if err != nil { - log.Error(err, "unable to parse template at key", "key", k) - continue + return fmt.Errorf(errParse, k, err) } buf := bytes.NewBuffer(nil) err = t.Execute(buf, data) if err != nil { - log.Error(err, "unable to execute template at key", "key", k) - continue + return fmt.Errorf(errExecute, k, err) } secret.Data[k] = buf.Bytes() } return nil } -func pkcs12keyPass(pass string, input []byte) []byte { +func pkcs12keyPass(pass string, input []byte) ([]byte, error) { key, _, err := pkcs12.Decode(input, pass) if err != nil { - log.Error(err, "unable to decode pkcs12 with password") - return nil + return nil, fmt.Errorf(errDecodePKCS12WithPass, err) } kb, err := pkcs8.ConvertPrivateKeyToPKCS8(key) if err != nil { - log.Error(err, "unable to convert pkcs12 private key") - return nil + return nil, fmt.Errorf(errConvertPrivKey, err) } - return kb + return kb, nil } -func pkcs12key(input []byte) []byte { +func pkcs12key(input []byte) ([]byte, error) { return pkcs12keyPass("", input) } -func pkcs12certPass(pass string, input []byte) []byte { +func pkcs12certPass(pass string, input []byte) ([]byte, error) { _, cert, err := pkcs12.Decode(input, pass) if err != nil { - log.Error(err, "unable to decode pkcs12 certificate with password") - return nil + return nil, fmt.Errorf(errDecodeCertWithPass, err) } - return cert.Raw + return cert.Raw, nil } -func pkcs12cert(input []byte) []byte { +func pkcs12cert(input []byte) ([]byte, error) { return pkcs12certPass("", input) } -func pemPrivateKey(key []byte) string { +func pemPrivateKey(key []byte) (string, error) { buf := bytes.NewBuffer(nil) err := pem.Encode(buf, &pem.Block{Type: "PRIVATE KEY", Bytes: key}) if err != nil { - log.Error(err, "unable to encode pem private key") - return "" + return "", fmt.Errorf(errEncodePEMKey, err) } - return buf.String() + return buf.String(), err } -func pemCertificate(cert []byte) string { +func pemCertificate(cert []byte) (string, error) { buf := bytes.NewBuffer(nil) err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) if err != nil { - log.Error(err, "unable to encode pem certificate") - return "" + return "", fmt.Errorf(errEncodePEMCert, err) } - return buf.String() + return buf.String(), nil } -func base64decode(in []byte) []byte { +func base64decode(in []byte) ([]byte, error) { out := make([]byte, len(in)) l, err := base64.StdEncoding.Decode(out, in) if err != nil { - log.Error(err, "unable to encode base64") - return []byte("") + return nil, fmt.Errorf(errDecodeBase64, err) } - return out[:l] + return out[:l], nil } func base64encode(in []byte) []byte { @@ -136,21 +139,21 @@ func base64encode(in []byte) []byte { return out } -func fromJSON(in []byte) interface{} { +func fromJSON(in []byte) (interface{}, error) { var out interface{} err := json.Unmarshal(in, &out) if err != nil { - log.Error(err, "unable to unmarshal json") + return nil, fmt.Errorf(errUnmarshalJSON, err) } - return out + return out, nil } -func toJSON(in interface{}) string { +func toJSON(in interface{}) (string, error) { output, err := json.Marshal(in) if err != nil { - log.Error(err, "unable to marshal json") + return "", fmt.Errorf(errMarshalJSON, err) } - return string(output) + return string(output), nil } func toString(in []byte) string { diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index ce5a21820..b3221394d 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -128,13 +128,13 @@ func TestExecute(t *testing.T) { name: "multiline template", secret: map[string][]byte{ "cfg": []byte(` -datasources: -- name: Graphite - type: graphite - access: proxy - url: http://localhost:8080 - password: "{{ .password | toString }}" - user: "{{ .user | toString }}"`), + datasources: + - name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + password: "{{ .password | toString }}" + user: "{{ .user | toString }}"`), }, data: map[string][]byte{ "user": []byte(`foobert`), @@ -142,13 +142,13 @@ datasources: }, outSecret: map[string][]byte{ "cfg": []byte(` -datasources: -- name: Graphite - type: graphite - access: proxy - url: http://localhost:8080 - password: "harharhar" - user: "foobert"`), + datasources: + - name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + password: "harharhar" + user: "foobert"`), }, }, { @@ -189,6 +189,52 @@ datasources: "cert": []byte(pkcs12Cert), }, }, + { + name: "base64 decode error", + secret: map[string][]byte{ + "key": []byte(`{{ .example | base64decode }}`), + }, + data: map[string][]byte{ + "example": []byte("iam_no_base64"), + }, + expErr: "unable to decode base64", + }, + { + name: "pkcs12 key wrong password", + secret: map[string][]byte{ + "key": []byte(`{{ .secret | base64decode | pkcs12keyPass "wrong" | pemPrivateKey }}`), + }, + data: map[string][]byte{ + "secret": []byte(pkcs12ContentWithPass), + }, + expErr: "unable to decode pkcs12", + }, + { + name: "pkcs12 cert wrong password", + secret: map[string][]byte{ + "cert": []byte(`{{ .secret | base64decode | pkcs12certPass "wrong" | pemCertificate }}`), + }, + data: map[string][]byte{ + "secret": []byte(pkcs12ContentWithPass), + }, + expErr: "unable to decode pkcs12", + }, + { + name: "fromJSON error", + secret: map[string][]byte{ + "key": []byte(`{{ "{ # no json # }" | toBytes | fromJSON }}`), + }, + data: map[string][]byte{}, + expErr: "unable to unmarshal json", + }, + { + name: "template syntax error", + secret: map[string][]byte{ + "key": []byte(`{{ #xx }}`), + }, + data: map[string][]byte{}, + expErr: "unable to parse template", + }, } for i := range tbl { @@ -198,7 +244,7 @@ datasources: Data: row.secret, }, row.data) if !ErrorContains(err, row.expErr) { - t.Errorf("unexpected error: %s", err) + t.Errorf("unexpected error: %s, expected: %s", err, row.expErr) } if row.outSecret == nil { return