mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
parent
b843b5f149
commit
5f719a3dce
2 changed files with 101 additions and 52 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue