mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Port encrypted config shared bits to a separate PR (#9037)
* Port encrypted config shared bits to a separate PR * Address feedback
This commit is contained in:
2
go.mod
2
go.mod
@@ -45,7 +45,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-test/deep v1.0.2
|
||||
github.com/gocql/gocql v0.0.0-20190402132108-0e1d5de854df
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/protobuf v1.4.1
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/google/go-metrics-stackdriver v0.2.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -508,8 +508,6 @@ github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.1.2 h1:X9eK6NSb1qafvoE
|
||||
github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.1.2/go.mod h1:YRW9zn9NZNitRlPYNAWRp/YEdKCF/X8aOg8IYSxFT5Y=
|
||||
github.com/hashicorp/vault-plugin-secrets-openldap v0.1.0-beta1.0.20200515114020-e19ec0ccabc3 h1:7SLyjXdy1QwQgIb2XU39pr0J0W0fZxKq/pm8M3pv05Y=
|
||||
github.com/hashicorp/vault-plugin-secrets-openldap v0.1.0-beta1.0.20200515114020-e19ec0ccabc3/go.mod h1:mfMeH+oOuVMgJVQahScA7ic+q8HfzHTocE3xJhmk4Co=
|
||||
github.com/hashicorp/vault-plugin-secrets-openldap v0.1.2 h1:618nyNUHX2Oc7pcQh6r0Zm0kaMrhkfAyUyFmDFwyYnQ=
|
||||
github.com/hashicorp/vault-plugin-secrets-openldap v0.1.2/go.mod h1:9Cy4Jp779BjuIOhYLjEfH3M3QCUxZgPnvJ3tAOOmof4=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
|
||||
@@ -49,6 +49,15 @@ func LoadConfigFile(path string) (*SharedConfig, error) {
|
||||
return ParseConfig(string(d))
|
||||
}
|
||||
|
||||
func LoadConfigKMSes(path string) ([]*KMS, error) {
|
||||
// Read the file
|
||||
d, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseKMSes(string(d))
|
||||
}
|
||||
|
||||
func ParseConfig(d string) (*SharedConfig, error) {
|
||||
// Parse!
|
||||
obj, err := hcl.Parse(d)
|
||||
@@ -82,19 +91,19 @@ func ParseConfig(d string) (*SharedConfig, error) {
|
||||
}
|
||||
|
||||
if o := list.Filter("hsm"); len(o.Items) > 0 {
|
||||
if err := parseKMS(&result, o, "hsm", 1); err != nil {
|
||||
if err := parseKMS(&result.Seals, o, "hsm", 2); err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing 'hsm': {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
if o := list.Filter("seal"); len(o.Items) > 0 {
|
||||
if err := parseKMS(&result, o, "seal", 2); err != nil {
|
||||
if err := parseKMS(&result.Seals, o, "seal", 3); err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing 'seal': {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
if o := list.Filter("kms"); len(o.Items) > 0 {
|
||||
if err := parseKMS(&result, o, "kms", 2); err != nil {
|
||||
if err := parseKMS(&result.Seals, o, "kms", 3); err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing 'kms': {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
92
internalshared/configutil/encrypt_decrypt.go
Normal file
92
internalshared/configutil/encrypt_decrypt.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package configutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
encryptRegex = regexp.MustCompile(`{{encrypt\(.*\)}}`)
|
||||
decryptRegex = regexp.MustCompile(`{{decrypt\(.*\)}}`)
|
||||
)
|
||||
|
||||
func EncryptDecrypt(rawStr string, decrypt, strip bool, wrapper wrapping.Wrapper) (string, error) {
|
||||
var locs [][]int
|
||||
raw := []byte(rawStr)
|
||||
searchVal := "{{encrypt("
|
||||
replaceVal := "{{decrypt("
|
||||
suffixVal := ")}}"
|
||||
if decrypt {
|
||||
searchVal = "{{decrypt("
|
||||
replaceVal = "{{encrypt("
|
||||
locs = decryptRegex.FindAllIndex(raw, -1)
|
||||
} else {
|
||||
locs = encryptRegex.FindAllIndex(raw, -1)
|
||||
}
|
||||
if strip {
|
||||
replaceVal = ""
|
||||
suffixVal = ""
|
||||
}
|
||||
|
||||
out := make([]byte, 0, len(rawStr)*2)
|
||||
var prevMaxLoc int
|
||||
for _, match := range locs {
|
||||
if len(match) != 2 {
|
||||
return "", fmt.Errorf("expected two values for match, got %d", len(match))
|
||||
}
|
||||
|
||||
// Append everything from the end of the last match to the beginning of this one
|
||||
out = append(out, raw[prevMaxLoc:match[0]]...)
|
||||
|
||||
// Transform. First pull off the suffix/prefix
|
||||
matchBytes := raw[match[0]:match[1]]
|
||||
matchBytes = bytes.TrimSuffix(bytes.TrimPrefix(matchBytes, []byte(searchVal)), []byte(")}}"))
|
||||
var finalVal string
|
||||
|
||||
// Now encrypt or decrypt
|
||||
switch decrypt {
|
||||
case false:
|
||||
outBlob, err := wrapper.Encrypt(context.Background(), matchBytes, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error encrypting parameter: %w", err)
|
||||
}
|
||||
if outBlob == nil {
|
||||
return "", errors.New("nil value returned from encrypting parameter")
|
||||
}
|
||||
outMsg, err := proto.Marshal(outBlob)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error marshaling encrypted parameter: %w", err)
|
||||
}
|
||||
finalVal = base64.RawURLEncoding.EncodeToString(outMsg)
|
||||
|
||||
default:
|
||||
inMsg, err := base64.RawURLEncoding.DecodeString(string(matchBytes))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decoding encrypted parameter: %w", err)
|
||||
}
|
||||
inBlob := new(wrapping.EncryptedBlobInfo)
|
||||
if err := proto.Unmarshal(inMsg, inBlob); err != nil {
|
||||
return "", fmt.Errorf("error unmarshaling encrypted parameter: %w", err)
|
||||
}
|
||||
dec, err := wrapper.Decrypt(context.Background(), inBlob, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error decrypting encrypted parameter: %w", err)
|
||||
}
|
||||
finalVal = string(dec)
|
||||
}
|
||||
|
||||
// Append new value
|
||||
out = append(out, []byte(fmt.Sprintf("%s%s%s", replaceVal, finalVal, suffixVal))...)
|
||||
prevMaxLoc = match[1]
|
||||
}
|
||||
// At the end, append the rest
|
||||
out = append(out, raw[prevMaxLoc:len(raw)]...)
|
||||
return string(out), nil
|
||||
}
|
||||
111
internalshared/configutil/encrypt_decrypt_test.go
Normal file
111
internalshared/configutil/encrypt_decrypt_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package configutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func getAEADTestKMS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncryptParams(t *testing.T) {
|
||||
rawStr := `
|
||||
storage "consul" {
|
||||
api_key = "{{encrypt(foobar)}}"
|
||||
}
|
||||
|
||||
telemetry {
|
||||
some_param = "something"
|
||||
circonus_api_key = "{{encrypt(barfoo)}}"
|
||||
}
|
||||
`
|
||||
|
||||
finalStr := `
|
||||
storage "consul" {
|
||||
api_key = "foobar"
|
||||
}
|
||||
|
||||
telemetry {
|
||||
some_param = "something"
|
||||
circonus_api_key = "barfoo"
|
||||
}
|
||||
`
|
||||
|
||||
reverser := new(reversingWrapper)
|
||||
out, err := EncryptDecrypt(rawStr, false, false, reverser)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
first := true
|
||||
locs := decryptRegex.FindAllIndex([]byte(out), -1)
|
||||
for _, match := range locs {
|
||||
matchBytes := []byte(out)[match[0]:match[1]]
|
||||
matchBytes = bytes.TrimSuffix(bytes.TrimPrefix(matchBytes, []byte("{{decrypt(")), []byte(")}}"))
|
||||
inMsg, err := base64.RawURLEncoding.DecodeString(string(matchBytes))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inBlob := new(wrapping.EncryptedBlobInfo)
|
||||
if err := proto.Unmarshal(inMsg, inBlob); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ct := string(inBlob.Ciphertext)
|
||||
if first {
|
||||
if ct != "raboof" {
|
||||
t.Fatal(ct)
|
||||
}
|
||||
first = false
|
||||
} else {
|
||||
if ct != "oofrab" {
|
||||
t.Fatal(ct)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decOut, err := EncryptDecrypt(out, true, false, reverser)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if decOut != rawStr {
|
||||
t.Fatal(decOut)
|
||||
}
|
||||
|
||||
decOut, err = EncryptDecrypt(out, true, true, reverser)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if decOut != finalStr {
|
||||
t.Fatal(decOut)
|
||||
}
|
||||
}
|
||||
|
||||
type reversingWrapper struct{}
|
||||
|
||||
func (r *reversingWrapper) Type() string { return "reversing" }
|
||||
func (r *reversingWrapper) KeyID() string { return "reverser" }
|
||||
func (r *reversingWrapper) HMACKeyID() string { return "" }
|
||||
func (r *reversingWrapper) Init(_ context.Context) error { return nil }
|
||||
func (r *reversingWrapper) Finalize(_ context.Context) error { return nil }
|
||||
func (r *reversingWrapper) Encrypt(_ context.Context, input []byte, _ []byte) (*wrapping.EncryptedBlobInfo, error) {
|
||||
return &wrapping.EncryptedBlobInfo{
|
||||
Ciphertext: r.reverse(input),
|
||||
}, nil
|
||||
}
|
||||
func (r *reversingWrapper) Decrypt(_ context.Context, input *wrapping.EncryptedBlobInfo, _ []byte) ([]byte, error) {
|
||||
return r.reverse(input.Ciphertext), nil
|
||||
}
|
||||
func (r *reversingWrapper) reverse(input []byte) []byte {
|
||||
output := make([]byte, len(input))
|
||||
for i, j := 0, len(input)-1; i < j; i, j = i+1, j-1 {
|
||||
output[i], output[j] = input[j], input[i]
|
||||
}
|
||||
return output
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func (k *KMS) GoString() string {
|
||||
return fmt.Sprintf("*%#v", *k)
|
||||
}
|
||||
|
||||
func parseKMS(result *SharedConfig, list *ast.ObjectList, blockName string, maxKMS int) error {
|
||||
func parseKMS(result *[]*KMS, list *ast.ObjectList, blockName string, maxKMS int) error {
|
||||
if len(list.Items) > maxKMS {
|
||||
return fmt.Errorf("only two or less %q blocks are permitted", blockName)
|
||||
}
|
||||
@@ -117,11 +117,47 @@ func parseKMS(result *SharedConfig, list *ast.ObjectList, blockName string, maxK
|
||||
seals = append(seals, seal)
|
||||
}
|
||||
|
||||
result.Seals = append(result.Seals, seals...)
|
||||
*result = append(*result, seals...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseKMSes(d string) ([]*KMS, error) {
|
||||
// Parse!
|
||||
obj, err := hcl.Parse(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start building the result
|
||||
var result struct {
|
||||
Seals []*KMS `hcl:"-"`
|
||||
}
|
||||
|
||||
if err := hcl.DecodeObject(&result, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list, ok := obj.Node.(*ast.ObjectList)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
|
||||
}
|
||||
|
||||
if o := list.Filter("seal"); len(o.Items) > 0 {
|
||||
if err := parseKMS(&result.Seals, o, "seal", 3); err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing 'seal': {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
if o := list.Filter("kms"); len(o.Items) > 0 {
|
||||
if err := parseKMS(&result.Seals, o, "kms", 3); err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing 'kms': {{err}}", err)
|
||||
}
|
||||
}
|
||||
|
||||
return result.Seals, nil
|
||||
}
|
||||
|
||||
func configureWrapper(configKMS *KMS, infoKeys *[]string, info *map[string]string, logger hclog.Logger) (wrapping.Wrapper, error) {
|
||||
var wrapper wrapping.Wrapper
|
||||
var kmsInfo map[string]string
|
||||
@@ -167,9 +203,11 @@ func configureWrapper(configKMS *KMS, infoKeys *[]string, info *map[string]strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range kmsInfo {
|
||||
*infoKeys = append(*infoKeys, k)
|
||||
(*info)[k] = v
|
||||
if infoKeys != nil && info != nil {
|
||||
for k, v := range kmsInfo {
|
||||
*infoKeys = append(*infoKeys, k)
|
||||
(*info)[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return wrapper, nil
|
||||
|
||||
Reference in New Issue
Block a user