1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

fix(tpl): fail on parse/execute error

fixes #126
This commit is contained in:
Moritz Johner 2021-05-01 21:49:12 +02:00
parent b843b5f149
commit 5f719a3dce
2 changed files with 101 additions and 52 deletions

View file

@ -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 {

View file

@ -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