storage: transformers: pass a context.Context

When an envelope transformer calls out to KMS (for instance), it will be
very helpful to pass a `context.Context` to allow for cancellation. This
patch does that, while passing the previously-expected additional data
via a context value.

Signed-off-by: Steve Kuznetsov <skuznets@redhat.com>

Kubernetes-commit: 27312feb9983c18d1daf00afba788727d024cdd0
This commit is contained in:
Steve Kuznetsov 2022-02-17 07:29:44 -08:00 committed by Kubernetes Publisher
parent 0d701a6123
commit af1cb1cefe
14 changed files with 167 additions and 139 deletions

View File

@ -18,6 +18,7 @@ package encryptionconfig
import (
"bytes"
"context"
"encoding/base64"
"errors"
"io"
@ -184,7 +185,8 @@ func TestEncryptionProviderConfigCorrect(t *testing.T) {
secretboxFirstTransformer := secretboxFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
kmsFirstTransformer := kmsFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
context := value.DefaultContext([]byte(sampleContextText))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte(sampleContextText))
originalText := []byte(sampleText)
transformers := []struct {
@ -199,13 +201,13 @@ func TestEncryptionProviderConfigCorrect(t *testing.T) {
}
for _, testCase := range transformers {
transformedData, err := testCase.Transformer.TransformToStorage(originalText, context)
transformedData, err := testCase.Transformer.TransformToStorage(ctx, originalText, dataCtx)
if err != nil {
t.Fatalf("%s: error while transforming data to storage: %s", testCase.Name, err)
}
for _, transformer := range transformers {
untransformedData, stale, err := transformer.Transformer.TransformFromStorage(transformedData, context)
untransformedData, stale, err := transformer.Transformer.TransformFromStorage(ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("%s: error while reading using %s transformer: %s", testCase.Name, transformer.Name, err)
}
@ -347,16 +349,17 @@ func TestCBCKeyRotationWithoutOverlappingProviders(t *testing.T) {
func testCBCKeyRotationWithProviders(t *testing.T, firstEncryptionConfig, firstPrefix, secondEncryptionConfig, secondPrefix string) {
p := getTransformerFromEncryptionConfig(t, firstEncryptionConfig)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
out, err := p.TransformToStorage([]byte("firstvalue"), context)
out, err := p.TransformToStorage(ctx, []byte("firstvalue"), dataCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(out, []byte(firstPrefix)) {
t.Fatalf("unexpected prefix: %q", out)
}
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -365,14 +368,14 @@ func testCBCKeyRotationWithProviders(t *testing.T, firstEncryptionConfig, firstP
}
// verify changing the context fails storage
_, _, err = p.TransformFromStorage(out, value.DefaultContext([]byte("incorrect_context")))
_, _, err = p.TransformFromStorage(ctx, out, value.DefaultContext([]byte("incorrect_context")))
if err != nil {
t.Fatalf("CBC mode does not support authentication: %v", err)
}
// reverse the order, use the second key
p = getTransformerFromEncryptionConfig(t, secondEncryptionConfig)
from, stale, err = p.TransformFromStorage(out, context)
from, stale, err = p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -380,14 +383,14 @@ func testCBCKeyRotationWithProviders(t *testing.T, firstEncryptionConfig, firstP
t.Fatalf("unexpected data: %t %q", stale, from)
}
out, err = p.TransformToStorage([]byte("firstvalue"), context)
out, err = p.TransformToStorage(ctx, []byte("firstvalue"), dataCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(out, []byte(secondPrefix)) {
t.Fatalf("unexpected prefix: %q", out)
}
from, stale, err = p.TransformFromStorage(out, context)
from, stale, err = p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}

View File

@ -136,7 +136,7 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou
}
kv := getResp.Kvs[0]
data, _, err := s.transformer.TransformFromStorage(kv.Value, authenticatedDataString(key))
data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(key))
if err != nil {
return storage.NewInternalError(err.Error())
}
@ -163,7 +163,7 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object,
return err
}
newData, err := s.transformer.TransformToStorage(data, authenticatedDataString(key))
newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(key))
if err != nil {
return storage.NewInternalError(err.Error())
}
@ -211,7 +211,7 @@ func (s *store) conditionalDelete(
if err != nil {
return nil, err
}
return s.getState(getResp, key, v, false)
return s.getState(ctx, getResp, key, v, false)
}
var origState *objState
@ -296,7 +296,7 @@ func (s *store) conditionalDelete(
if !txnResp.Succeeded {
getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
klog.V(4).Infof("deletion of %s failed because of a conflict, going to retry", key)
origState, err = s.getState(getResp, key, v, false)
origState, err = s.getState(ctx, getResp, key, v, false)
if err != nil {
return err
}
@ -327,7 +327,7 @@ func (s *store) GuaranteedUpdate(
if err != nil {
return nil, err
}
return s.getState(getResp, key, v, ignoreNotFound)
return s.getState(ctx, getResp, key, v, ignoreNotFound)
}
var origState *objState
@ -415,7 +415,7 @@ func (s *store) GuaranteedUpdate(
}
}
newData, err := s.transformer.TransformToStorage(data, transformContext)
newData, err := s.transformer.TransformToStorage(ctx, data, transformContext)
if err != nil {
return storage.NewInternalError(err.Error())
}
@ -442,7 +442,7 @@ func (s *store) GuaranteedUpdate(
if !txnResp.Succeeded {
getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key)
origState, err = s.getState(getResp, key, v, ignoreNotFound)
origState, err = s.getState(ctx, getResp, key, v, ignoreNotFound)
if err != nil {
return err
}
@ -728,7 +728,7 @@ func (s *store) list(ctx context.Context, key string, opts storage.ListOptions,
}
lastKey = kv.Key
data, _, err := s.transformer.TransformFromStorage(kv.Value, authenticatedDataString(kv.Key))
data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(kv.Key))
if err != nil {
return storage.NewInternalErrorf("unable to transform key %q: %v", kv.Key, err)
}
@ -828,7 +828,7 @@ func (s *store) Watch(ctx context.Context, key string, opts storage.ListOptions)
return s.watcher.Watch(ctx, key, int64(rev), opts.Recursive, opts.ProgressNotify, opts.Predicate)
}
func (s *store) getState(getResp *clientv3.GetResponse, key string, v reflect.Value, ignoreNotFound bool) (*objState, error) {
func (s *store) getState(ctx context.Context, getResp *clientv3.GetResponse, key string, v reflect.Value, ignoreNotFound bool) (*objState, error) {
state := &objState{
meta: &storage.ResponseMeta{},
}
@ -847,7 +847,7 @@ func (s *store) getState(getResp *clientv3.GetResponse, key string, v reflect.Va
return nil, err
}
} else {
data, stale, err := s.transformer.TransformFromStorage(getResp.Kvs[0].Value, authenticatedDataString(key))
data, stale, err := s.transformer.TransformFromStorage(ctx, getResp.Kvs[0].Value, authenticatedDataString(key))
if err != nil {
return nil, storage.NewInternalError(err.Error())
}

View File

@ -79,24 +79,24 @@ type prefixTransformer struct {
reads uint64
}
func (p *prefixTransformer) TransformFromStorage(b []byte, ctx value.Context) ([]byte, bool, error) {
func (p *prefixTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
atomic.AddUint64(&p.reads, 1)
if ctx == nil {
if dataCtx == nil {
panic("no context provided")
}
if !bytes.HasPrefix(b, p.prefix) {
return nil, false, fmt.Errorf("value does not have expected prefix %q: %s,", p.prefix, string(b))
if !bytes.HasPrefix(data, p.prefix) {
return nil, false, fmt.Errorf("value does not have expected prefix %q: %s,", p.prefix, string(data))
}
return bytes.TrimPrefix(b, p.prefix), p.stale, p.err
return bytes.TrimPrefix(data, p.prefix), p.stale, p.err
}
func (p *prefixTransformer) TransformToStorage(b []byte, ctx value.Context) ([]byte, error) {
if ctx == nil {
func (p *prefixTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
if dataCtx == nil {
panic("no context provided")
}
if len(b) > 0 {
return append(append([]byte{}, p.prefix...), b...), p.err
if len(data) > 0 {
return append(append([]byte{}, p.prefix...), data...), p.err
}
return b, p.err
return data, p.err
}
func (p *prefixTransformer) resetReads() {
@ -2231,18 +2231,18 @@ type fancyTransformer struct {
index int
}
func (t *fancyTransformer) TransformFromStorage(b []byte, ctx value.Context) ([]byte, bool, error) {
if err := t.createObject(); err != nil {
func (t *fancyTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
if err := t.createObject(ctx); err != nil {
return nil, false, err
}
return t.transformer.TransformFromStorage(b, ctx)
return t.transformer.TransformFromStorage(ctx, data, dataCtx)
}
func (t *fancyTransformer) TransformToStorage(b []byte, ctx value.Context) ([]byte, error) {
return t.transformer.TransformToStorage(b, ctx)
func (t *fancyTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
return t.transformer.TransformToStorage(ctx, data, dataCtx)
}
func (t *fancyTransformer) createObject() error {
func (t *fancyTransformer) createObject(ctx context.Context) error {
t.lock.Lock()
defer t.lock.Unlock()
@ -2257,7 +2257,7 @@ func (t *fancyTransformer) createObject() error {
},
}
out := &example.Pod{}
return t.store.Create(context.TODO(), key, obj, out, 0)
return t.store.Create(ctx, key, obj, out, 0)
}
func TestConsistentList(t *testing.T) {
@ -2271,7 +2271,7 @@ func TestConsistentList(t *testing.T) {
transformer.store = store
for i := 0; i < 5; i++ {
if err := transformer.createObject(); err != nil {
if err := transformer.createObject(context.TODO()); err != nil {
t.Fatalf("failed to create object: %v", err)
}
}

View File

@ -426,7 +426,7 @@ func (wc *watchChan) prepareObjs(e *event) (curObj runtime.Object, oldObj runtim
}
if !e.isDeleted {
data, _, err := wc.watcher.transformer.TransformFromStorage(e.value, authenticatedDataString(e.key))
data, _, err := wc.watcher.transformer.TransformFromStorage(wc.ctx, e.value, authenticatedDataString(e.key))
if err != nil {
return nil, nil, err
}
@ -441,7 +441,7 @@ func (wc *watchChan) prepareObjs(e *event) (curObj runtime.Object, oldObj runtim
// we need the object only to compute whether it was filtered out
// before).
if len(e.prevValue) > 0 && (e.isDeleted || !wc.acceptAll()) {
data, _, err := wc.watcher.transformer.TransformFromStorage(e.prevValue, authenticatedDataString(e.key))
data, _, err := wc.watcher.transformer.TransformFromStorage(wc.ctx, e.prevValue, authenticatedDataString(e.key))
if err != nil {
return nil, nil, err
}

View File

@ -19,6 +19,7 @@ package aes
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
@ -52,7 +53,7 @@ func NewGCMTransformer(block cipher.Block) value.Transformer {
return &gcm{block: block}
}
func (t *gcm) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
func (t *gcm) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
aead, err := cipher.NewGCM(t.block)
if err != nil {
return nil, false, err
@ -61,11 +62,11 @@ func (t *gcm) TransformFromStorage(data []byte, context value.Context) ([]byte,
if len(data) < nonceSize {
return nil, false, fmt.Errorf("the stored data was shorter than the required size")
}
result, err := aead.Open(nil, data[:nonceSize], data[nonceSize:], context.AuthenticatedData())
result, err := aead.Open(nil, data[:nonceSize], data[nonceSize:], dataCtx.AuthenticatedData())
return result, false, err
}
func (t *gcm) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
func (t *gcm) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
aead, err := cipher.NewGCM(t.block)
if err != nil {
return nil, err
@ -79,7 +80,7 @@ func (t *gcm) TransformToStorage(data []byte, context value.Context) ([]byte, er
if n != nonceSize {
return nil, fmt.Errorf("unable to read sufficient random bytes")
}
cipherText := aead.Seal(result[nonceSize:nonceSize], result[:nonceSize], data, context.AuthenticatedData())
cipherText := aead.Seal(result[nonceSize:nonceSize], result[:nonceSize], data, dataCtx.AuthenticatedData())
return result[:nonceSize+len(cipherText)], nil
}
@ -100,7 +101,7 @@ var (
errInvalidPKCS7Padding = errors.New("invalid padding on input")
)
func (t *cbc) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
func (t *cbc) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
blockSize := aes.BlockSize
if len(data) < blockSize {
return nil, false, fmt.Errorf("the stored data was shorter than the required size")
@ -133,7 +134,7 @@ func (t *cbc) TransformFromStorage(data []byte, context value.Context) ([]byte,
return result[:size], false, nil
}
func (t *cbc) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
func (t *cbc) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
blockSize := aes.BlockSize
paddingSize := blockSize - (len(data) % blockSize)
result := make([]byte, blockSize+len(data)+paddingSize)

View File

@ -18,6 +18,7 @@ package aes
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
@ -56,20 +57,21 @@ func TestGCMKeyRotation(t *testing.T) {
t.Fatal(err)
}
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
p := value.NewPrefixTransformers(testErr,
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewGCMTransformer(block1)},
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewGCMTransformer(block2)},
)
out, err := p.TransformToStorage([]byte("firstvalue"), context)
out, err := p.TransformToStorage(ctx, []byte("firstvalue"), dataCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(out, []byte("first:")) {
t.Fatalf("unexpected prefix: %q", out)
}
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -78,7 +80,7 @@ func TestGCMKeyRotation(t *testing.T) {
}
// verify changing the context fails storage
_, _, err = p.TransformFromStorage(out, value.DefaultContext([]byte("incorrect_context")))
_, _, err = p.TransformFromStorage(ctx, out, value.DefaultContext([]byte("incorrect_context")))
if err == nil {
t.Fatalf("expected unauthenticated data")
}
@ -88,7 +90,7 @@ func TestGCMKeyRotation(t *testing.T) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewGCMTransformer(block2)},
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewGCMTransformer(block1)},
)
from, stale, err = p.TransformFromStorage(out, context)
from, stale, err = p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -108,20 +110,21 @@ func TestCBCKeyRotation(t *testing.T) {
t.Fatal(err)
}
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
p := value.NewPrefixTransformers(testErr,
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewCBCTransformer(block1)},
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewCBCTransformer(block2)},
)
out, err := p.TransformToStorage([]byte("firstvalue"), context)
out, err := p.TransformToStorage(ctx, []byte("firstvalue"), dataCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(out, []byte("first:")) {
t.Fatalf("unexpected prefix: %q", out)
}
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -130,7 +133,7 @@ func TestCBCKeyRotation(t *testing.T) {
}
// verify changing the context fails storage
_, _, err = p.TransformFromStorage(out, value.DefaultContext([]byte("incorrect_context")))
_, _, err = p.TransformFromStorage(ctx, out, value.DefaultContext([]byte("incorrect_context")))
if err != nil {
t.Fatalf("CBC mode does not support authentication: %v", err)
}
@ -140,7 +143,7 @@ func TestCBCKeyRotation(t *testing.T) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewCBCTransformer(block2)},
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewCBCTransformer(block1)},
)
from, stale, err = p.TransformFromStorage(out, context)
from, stale, err = p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -199,10 +202,11 @@ func benchmarkGCMRead(b *testing.B, keyLength int, valueLength int, expectStale
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewGCMTransformer(block2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
out, err := p.TransformToStorage(v, context)
out, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -216,7 +220,7 @@ func benchmarkGCMRead(b *testing.B, keyLength int, valueLength int, expectStale
b.ResetTimer()
for i := 0; i < b.N; i++ {
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -241,12 +245,13 @@ func benchmarkGCMWrite(b *testing.B, keyLength int, valueLength int) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewGCMTransformer(block2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := p.TransformToStorage(v, context)
_, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -302,10 +307,11 @@ func benchmarkCBCRead(b *testing.B, keyLength int, valueLength int, expectStale
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewCBCTransformer(block2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
out, err := p.TransformToStorage(v, context)
out, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -319,7 +325,7 @@ func benchmarkCBCRead(b *testing.B, keyLength int, valueLength int, expectStale
b.ResetTimer()
for i := 0; i < b.N; i++ {
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -344,12 +350,13 @@ func benchmarkCBCWrite(b *testing.B, keyLength int, valueLength int) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewCBCTransformer(block2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := p.TransformToStorage(v, context)
_, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -373,9 +380,10 @@ func TestRoundTrip(t *testing.T) {
t.Fatal(err)
}
ctx := context.Background()
tests := []struct {
name string
context value.Context
dataCtx value.Context
t value.Transformer
}{
{name: "GCM 16 byte key", t: NewGCMTransformer(aes16block)},
@ -385,9 +393,9 @@ func TestRoundTrip(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
context := tt.context
if context == nil {
context = value.DefaultContext("")
dataCtx := tt.dataCtx
if dataCtx == nil {
dataCtx = value.DefaultContext([]byte(""))
}
for _, l := range lengths {
data := make([]byte, l)
@ -396,13 +404,13 @@ func TestRoundTrip(t *testing.T) {
}
original := append([]byte{}, data...)
ciphertext, err := tt.t.TransformToStorage(data, context)
ciphertext, err := tt.t.TransformToStorage(ctx, data, dataCtx)
if err != nil {
t.Errorf("TransformToStorage error = %v", err)
continue
}
result, stale, err := tt.t.TransformFromStorage(ciphertext, context)
result, stale, err := tt.t.TransformFromStorage(ctx, ciphertext, dataCtx)
if err != nil {
t.Errorf("TransformFromStorage error = %v", err)
continue

View File

@ -18,6 +18,7 @@ limitations under the License.
package envelope
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
@ -79,7 +80,7 @@ func NewEnvelopeTransformer(envelopeService Service, cacheSize int, baseTransfor
}
// TransformFromStorage decrypts data encrypted by this transformer using envelope encryption.
func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
func (t *envelopeTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
recordArrival(fromStorageLabel, time.Now())
// Read the 16 bit length-of-DEK encoded at the start of the encrypted DEK. 16 bits can
@ -113,11 +114,11 @@ func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Co
}
}
return transformer.TransformFromStorage(encData, context)
return transformer.TransformFromStorage(ctx, encData, dataCtx)
}
// TransformToStorage encrypts data to be written to disk using envelope encryption.
func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
func (t *envelopeTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
recordArrival(toStorageLabel, time.Now())
newKey, err := generateKey(32)
if err != nil {
@ -137,7 +138,7 @@ func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Cont
return nil, err
}
result, err := transformer.TransformToStorage(data, context)
result, err := transformer.TransformToStorage(ctx, data, dataCtx)
if err != nil {
return nil, err
}

View File

@ -18,6 +18,7 @@ package envelope
import (
"bytes"
"context"
"crypto/aes"
"encoding/base64"
"encoding/binary"
@ -101,14 +102,15 @@ func TestEnvelopeCaching(t *testing.T) {
if err != nil {
t.Fatalf("failed to initialize envelope transformer: %v", err)
}
context := value.DefaultContext([]byte(testContextText))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte(testContextText))
originalText := []byte(testText)
transformedData, err := envelopeTransformer.TransformToStorage(originalText, context)
transformedData, err := envelopeTransformer.TransformToStorage(ctx, originalText, dataCtx)
if err != nil {
t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err)
}
untransformedData, _, err := envelopeTransformer.TransformFromStorage(transformedData, context)
untransformedData, _, err := envelopeTransformer.TransformFromStorage(ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err)
}
@ -118,7 +120,7 @@ func TestEnvelopeCaching(t *testing.T) {
envelopeService.SetDisabledStatus(tt.simulateKMSPluginFailure)
// Subsequent read for the same data should work fine due to caching.
untransformedData, _, err = envelopeTransformer.TransformFromStorage(transformedData, context)
untransformedData, _, err = envelopeTransformer.TransformFromStorage(ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("could not decrypt Envelope transformer's encrypted data using just cache: %v", err)
}
@ -135,7 +137,8 @@ func TestEnvelopeCacheLimit(t *testing.T) {
if err != nil {
t.Fatalf("failed to initialize envelope transformer: %v", err)
}
context := value.DefaultContext([]byte(testContextText))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte(testContextText))
transformedOutputs := map[int][]byte{}
@ -143,7 +146,7 @@ func TestEnvelopeCacheLimit(t *testing.T) {
for i := 0; i < 2*testEnvelopeCacheSize; i++ {
numberText := []byte(strconv.Itoa(i))
res, err := envelopeTransformer.TransformToStorage(numberText, context)
res, err := envelopeTransformer.TransformToStorage(ctx, numberText, dataCtx)
transformedOutputs[i] = res
if err != nil {
t.Fatalf("envelopeTransformer: error while transforming data (%v) to storage: %s", numberText, err)
@ -154,7 +157,7 @@ func TestEnvelopeCacheLimit(t *testing.T) {
for i := 0; i < 2*testEnvelopeCacheSize; i++ {
numberText := []byte(strconv.Itoa(i))
output, _, err := envelopeTransformer.TransformFromStorage(transformedOutputs[i], context)
output, _, err := envelopeTransformer.TransformFromStorage(ctx, transformedOutputs[i], dataCtx)
if err != nil {
t.Fatalf("envelopeTransformer: error while transforming data (%v) from storage: %s", transformedOutputs[i], err)
}
@ -202,17 +205,18 @@ func BenchmarkAESGCMRead(b *testing.B) {
}
func benchmarkRead(b *testing.B, transformer value.Transformer, valueLength int) {
context := value.DefaultContext([]byte(testContextText))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte(testContextText))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
out, err := transformer.TransformToStorage(v, context)
out, err := transformer.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
from, stale, err := transformer.TransformFromStorage(out, context)
from, stale, err := transformer.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -230,14 +234,15 @@ func TestBackwardsCompatibility(t *testing.T) {
if err != nil {
t.Fatalf("failed to initialize envelope transformer: %v", err)
}
context := value.DefaultContext([]byte(testContextText))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte(testContextText))
originalText := []byte(testText)
transformedData, err := oldTransformToStorage(envelopeTransformerInst.(*envelopeTransformer), originalText, context)
transformedData, err := oldTransformToStorage(ctx, envelopeTransformerInst.(*envelopeTransformer), originalText, dataCtx)
if err != nil {
t.Fatalf("envelopeTransformer: error while transforming data to storage: %s", err)
}
untransformedData, _, err := envelopeTransformerInst.TransformFromStorage(transformedData, context)
untransformedData, _, err := envelopeTransformerInst.TransformFromStorage(ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err)
}
@ -247,7 +252,7 @@ func TestBackwardsCompatibility(t *testing.T) {
envelopeService.SetDisabledStatus(true)
// Subsequent read for the same data should work fine due to caching.
untransformedData, _, err = envelopeTransformerInst.TransformFromStorage(transformedData, context)
untransformedData, _, err = envelopeTransformerInst.TransformFromStorage(ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("could not decrypt Envelope transformer's encrypted data using just cache: %v", err)
}
@ -257,7 +262,7 @@ func TestBackwardsCompatibility(t *testing.T) {
}
// remove after 1.13
func oldTransformToStorage(t *envelopeTransformer, data []byte, context value.Context) ([]byte, error) {
func oldTransformToStorage(ctx context.Context, t *envelopeTransformer, data []byte, dataCtx value.Context) ([]byte, error) {
newKey, err := generateKey(32)
if err != nil {
return nil, err
@ -282,7 +287,7 @@ func oldTransformToStorage(t *envelopeTransformer, data []byte, context value.Co
prefixedData := make([]byte, len(prefix), len(data)+len(prefix))
copy(prefixedData, prefix)
result, err := transformer.TransformToStorage(data, context)
result, err := transformer.TransformToStorage(ctx, data, dataCtx)
if err != nil {
return nil, err
}

View File

@ -18,6 +18,7 @@ package identity
import (
"bytes"
"context"
"fmt"
"k8s.io/apiserver/pkg/storage/value"
@ -34,17 +35,17 @@ func NewEncryptCheckTransformer() value.Transformer {
}
// TransformFromStorage returns the input bytes if the data is not encrypted
func (identityTransformer) TransformFromStorage(b []byte, context value.Context) ([]byte, bool, error) {
func (identityTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
// identityTransformer has to return an error if the data is encoded using another transformer.
// JSON data starts with '{'. Protobuf data has a prefix 'k8s[\x00-\xFF]'.
// Prefix 'k8s:enc:' is reserved for encrypted data on disk.
if bytes.HasPrefix(b, []byte("k8s:enc:")) {
if bytes.HasPrefix(data, []byte("k8s:enc:")) {
return []byte{}, false, fmt.Errorf("identity transformer tried to read encrypted data")
}
return b, false, nil
return data, false, nil
}
// TransformToStorage implements the Transformer interface for identityTransformer
func (identityTransformer) TransformToStorage(b []byte, context value.Context) ([]byte, error) {
return b, nil
func (identityTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
return data, nil
}

View File

@ -18,6 +18,7 @@ limitations under the License.
package secretbox
import (
"context"
"crypto/rand"
"fmt"
@ -41,7 +42,7 @@ func NewSecretboxTransformer(key [32]byte) value.Transformer {
return &secretboxTransformer{key: key}
}
func (t *secretboxTransformer) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
func (t *secretboxTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
if len(data) < (secretbox.Overhead + nonceSize) {
return nil, false, fmt.Errorf("the stored data was shorter than the required size")
}
@ -56,7 +57,7 @@ func (t *secretboxTransformer) TransformFromStorage(data []byte, context value.C
return result, false, nil
}
func (t *secretboxTransformer) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
func (t *secretboxTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
var nonce [nonceSize]byte
n, err := rand.Read(nonce[:])
if err != nil {

View File

@ -18,6 +18,7 @@ package secretbox
import (
"bytes"
"context"
"crypto/rand"
"encoding/hex"
"fmt"
@ -35,20 +36,21 @@ var (
func TestSecretboxKeyRotation(t *testing.T) {
testErr := fmt.Errorf("test error")
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
p := value.NewPrefixTransformers(testErr,
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewSecretboxTransformer(key1)},
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewSecretboxTransformer(key2)},
)
out, err := p.TransformToStorage([]byte("firstvalue"), context)
out, err := p.TransformToStorage(ctx, []byte("firstvalue"), dataCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(out, []byte("first:")) {
t.Fatalf("unexpected prefix: %q", out)
}
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -58,7 +60,7 @@ func TestSecretboxKeyRotation(t *testing.T) {
// verify changing the context does not fails storage
// Secretbox is not currently an authenticating store
_, _, err = p.TransformFromStorage(out, value.DefaultContext([]byte("incorrect_context")))
_, _, err = p.TransformFromStorage(ctx, out, value.DefaultContext([]byte("incorrect_context")))
if err != nil {
t.Fatalf("secretbox is not authenticated")
}
@ -68,7 +70,7 @@ func TestSecretboxKeyRotation(t *testing.T) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewSecretboxTransformer(key2)},
value.PrefixTransformer{Prefix: []byte("first:"), Transformer: NewSecretboxTransformer(key1)},
)
from, stale, err = p.TransformFromStorage(out, context)
from, stale, err = p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
t.Fatal(err)
}
@ -117,10 +119,11 @@ func benchmarkSecretboxRead(b *testing.B, keyLength int, valueLength int, expect
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewSecretboxTransformer(key2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
out, err := p.TransformToStorage(v, context)
out, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -134,7 +137,7 @@ func benchmarkSecretboxRead(b *testing.B, keyLength int, valueLength int, expect
b.ResetTimer()
for i := 0; i < b.N; i++ {
from, stale, err := p.TransformFromStorage(out, context)
from, stale, err := p.TransformFromStorage(ctx, out, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -151,12 +154,13 @@ func benchmarkSecretboxWrite(b *testing.B, keyLength int, valueLength int) {
value.PrefixTransformer{Prefix: []byte("second:"), Transformer: NewSecretboxTransformer(key2)},
)
context := value.DefaultContext([]byte("authenticated_data"))
ctx := context.Background()
dataCtx := value.DefaultContext([]byte("authenticated_data"))
v := bytes.Repeat([]byte("0123456789abcdef"), valueLength/16)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := p.TransformToStorage(v, context)
_, err := p.TransformToStorage(ctx, v, dataCtx)
if err != nil {
b.Fatal(err)
}
@ -167,18 +171,19 @@ func benchmarkSecretboxWrite(b *testing.B, keyLength int, valueLength int) {
func TestRoundTrip(t *testing.T) {
lengths := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 128, 1024}
ctx := context.Background()
tests := []struct {
name string
context value.Context
dataCtx value.Context
t value.Transformer
}{
{name: "Secretbox 32 byte key", t: NewSecretboxTransformer(key1)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
context := tt.context
if context == nil {
context = value.DefaultContext("")
dataCtx := tt.dataCtx
if dataCtx == nil {
dataCtx = value.DefaultContext([]byte(""))
}
for _, l := range lengths {
data := make([]byte, l)
@ -187,13 +192,13 @@ func TestRoundTrip(t *testing.T) {
}
original := append([]byte{}, data...)
ciphertext, err := tt.t.TransformToStorage(data, context)
ciphertext, err := tt.t.TransformToStorage(ctx, data, dataCtx)
if err != nil {
t.Errorf("TransformToStorage error = %v", err)
continue
}
result, stale, err := tt.t.TransformFromStorage(ciphertext, context)
result, stale, err := tt.t.TransformFromStorage(ctx, ciphertext, dataCtx)
if err != nil {
t.Errorf("TransformFromStorage error = %v", err)
continue

View File

@ -17,6 +17,7 @@ limitations under the License.
package value
import (
"context"
"errors"
"strings"
"testing"
@ -102,8 +103,8 @@ func TestTotals(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
tt.prefix.TransformToStorage([]byte("value"), nil)
tt.prefix.TransformFromStorage([]byte("k8s:enc:kms:v1:value"), nil)
tt.prefix.TransformToStorage(context.Background(), []byte("value"), nil)
tt.prefix.TransformFromStorage(context.Background(), []byte("k8s:enc:kms:v1:value"), nil)
defer transformerOperationsTotal.Reset()
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(tt.want), tt.metrics...); err != nil {
t.Fatal(err)

View File

@ -19,6 +19,7 @@ package value
import (
"bytes"
"context"
"fmt"
"sync"
"time"
@ -45,9 +46,9 @@ type Transformer interface {
// TransformFromStorage may transform the provided data from its underlying storage representation or return an error.
// Stale is true if the object on disk is stale and a write to etcd should be issued, even if the contents of the object
// have not changed.
TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error)
TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, stale bool, err error)
// TransformToStorage may transform the provided data into the appropriate form in storage or return an error.
TransformToStorage(data []byte, context Context) (out []byte, err error)
TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error)
}
type identityTransformer struct{}
@ -55,11 +56,11 @@ type identityTransformer struct{}
// IdentityTransformer performs no transformation of the provided data.
var IdentityTransformer Transformer = identityTransformer{}
func (identityTransformer) TransformFromStorage(b []byte, ctx Context) ([]byte, bool, error) {
return b, false, nil
func (identityTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, bool, error) {
return data, false, nil
}
func (identityTransformer) TransformToStorage(b []byte, ctx Context) ([]byte, error) {
return b, nil
func (identityTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, error) {
return data, nil
}
// DefaultContext is a simple implementation of Context for a slice of bytes.
@ -86,17 +87,17 @@ func (t *MutableTransformer) Set(transformer Transformer) {
t.lock.Unlock()
}
func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) {
func (t *MutableTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, stale bool, err error) {
t.lock.RLock()
transformer := t.transformer
t.lock.RUnlock()
return transformer.TransformFromStorage(data, context)
return transformer.TransformFromStorage(ctx, data, dataCtx)
}
func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) {
func (t *MutableTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error) {
t.lock.RLock()
transformer := t.transformer
t.lock.RUnlock()
return transformer.TransformToStorage(data, context)
return transformer.TransformToStorage(ctx, data, dataCtx)
}
// PrefixTransformer holds a transformer interface and the prefix that the transformation is located under.
@ -129,12 +130,12 @@ func NewPrefixTransformers(err error, transformers ...PrefixTransformer) Transfo
// TransformFromStorage finds the first transformer with a prefix matching the provided data and returns
// the result of transforming the value. It will always mark any transformation as stale that is not using
// the first transformer.
func (t *prefixTransformers) TransformFromStorage(data []byte, context Context) ([]byte, bool, error) {
func (t *prefixTransformers) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, bool, error) {
start := time.Now()
var errs []error
for i, transformer := range t.transformers {
if bytes.HasPrefix(data, transformer.Prefix) {
result, stale, err := transformer.Transformer.TransformFromStorage(data[len(transformer.Prefix):], context)
result, stale, err := transformer.Transformer.TransformFromStorage(ctx, data[len(transformer.Prefix):], dataCtx)
// To migrate away from encryption, user can specify an identity transformer higher up
// (in the config file) than the encryption transformer. In that scenario, the identity transformer needs to
// identify (during reads from disk) whether the data being read is encrypted or not. If the data is encrypted,
@ -194,12 +195,12 @@ func (t *prefixTransformers) TransformFromStorage(data []byte, context Context)
}
// TransformToStorage uses the first transformer and adds its prefix to the data.
func (t *prefixTransformers) TransformToStorage(data []byte, context Context) ([]byte, error) {
func (t *prefixTransformers) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, error) {
start := time.Now()
transformer := t.transformers[0]
prefixedData := make([]byte, len(transformer.Prefix), len(data)+len(transformer.Prefix))
copy(prefixedData, transformer.Prefix)
result, err := transformer.Transformer.TransformToStorage(data, context)
result, err := transformer.Transformer.TransformToStorage(ctx, data, dataCtx)
RecordTransformation("to_storage", string(transformer.Prefix), start, err)
if err != nil {
return nil, err

View File

@ -18,6 +18,7 @@ package value
import (
"bytes"
"context"
"fmt"
"strings"
"testing"
@ -33,13 +34,13 @@ type testTransformer struct {
receivedFrom, receivedTo []byte
}
func (t *testTransformer) TransformFromStorage(from []byte, context Context) (data []byte, stale bool, err error) {
t.receivedFrom = from
func (t *testTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, stale bool, err error) {
t.receivedFrom = data
return t.from, t.stale, t.err
}
func (t *testTransformer) TransformToStorage(to []byte, context Context) (data []byte, err error) {
t.receivedTo = to
func (t *testTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error) {
t.receivedTo = data
return t.to, t.err
}
@ -68,7 +69,7 @@ func TestPrefixFrom(t *testing.T) {
{[]byte("stale:value"), []byte("value3"), true, nil, 3},
}
for i, test := range testCases {
got, stale, err := p.TransformFromStorage(test.input, nil)
got, stale, err := p.TransformFromStorage(context.Background(), test.input, nil)
if err != test.err || stale != test.stale || !bytes.Equal(got, test.expect) {
t.Errorf("%d: unexpected out: %q %t %#v", i, string(got), stale, err)
continue
@ -93,7 +94,7 @@ func TestPrefixTo(t *testing.T) {
}
for i, test := range testCases {
p := NewPrefixTransformers(testErr, test.transformers...)
got, err := p.TransformToStorage([]byte("value"), nil)
got, err := p.TransformToStorage(context.Background(), []byte("value"), nil)
if err != test.err || !bytes.Equal(got, test.expect) {
t.Errorf("%d: unexpected out: %q %#v", i, string(got), err)
continue
@ -183,7 +184,7 @@ func TestPrefixFromMetrics(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
tc.prefix.TransformFromStorage(tc.input, nil)
tc.prefix.TransformFromStorage(context.Background(), tc.input, nil)
defer transformerOperationsTotal.Reset()
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(tc.want), tc.metrics...); err != nil {
t.Fatal(err)
@ -241,7 +242,7 @@ func TestPrefixToMetrics(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
tc.prefix.TransformToStorage(tc.input, nil)
tc.prefix.TransformToStorage(context.Background(), tc.input, nil)
defer transformerOperationsTotal.Reset()
if err := testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(tc.want), tc.metrics...); err != nil {
t.Fatal(err)