[KMSv2] log request metadata as part of read/write

Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
This commit is contained in:
Anish Ramasekar 2023-02-24 23:06:30 +00:00
parent 70fee660fb
commit e40b3cf54e
No known key found for this signature in database
GPG Key ID: F1F7F3518F1ECB0C
2 changed files with 108 additions and 2 deletions

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/value" "k8s.io/apiserver/pkg/storage/value"
kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1" kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics" "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics"
@ -109,8 +110,12 @@ func (t *envelopeTransformer) TransformFromStorage(ctx context.Context, data []b
if transformer == nil { if transformer == nil {
value.RecordCacheMiss() value.RecordCacheMiss()
requestInfo := getRequestInfoFromContext(ctx)
uid := string(uuid.NewUUID()) uid := string(uuid.NewUUID())
klog.V(6).InfoS("Decrypting content using envelope service", "uid", uid, "key", string(dataCtx.AuthenticatedData())) klog.V(6).InfoS("decrypting content using envelope service", "uid", uid, "key", string(dataCtx.AuthenticatedData()),
"group", requestInfo.APIGroup, "version", requestInfo.APIVersion, "resource", requestInfo.Resource, "subresource", requestInfo.Subresource,
"verb", requestInfo.Verb, "namespace", requestInfo.Namespace, "name", requestInfo.Name)
key, err := t.envelopeService.Decrypt(ctx, uid, &kmsservice.DecryptRequest{ key, err := t.envelopeService.Decrypt(ctx, uid, &kmsservice.DecryptRequest{
Ciphertext: encryptedObject.EncryptedDEK, Ciphertext: encryptedObject.EncryptedDEK,
KeyID: encryptedObject.KeyID, KeyID: encryptedObject.KeyID,
@ -154,8 +159,11 @@ func (t *envelopeTransformer) TransformToStorage(ctx context.Context, data []byt
return nil, err return nil, err
} }
requestInfo := getRequestInfoFromContext(ctx)
uid := string(uuid.NewUUID()) uid := string(uuid.NewUUID())
klog.V(6).InfoS("encrypting content using envelope service", "uid", uid, "key", string(dataCtx.AuthenticatedData())) klog.V(6).InfoS("encrypting content using envelope service", "uid", uid, "key", string(dataCtx.AuthenticatedData()),
"group", requestInfo.APIGroup, "version", requestInfo.APIVersion, "resource", requestInfo.Resource, "subresource", requestInfo.Subresource,
"verb", requestInfo.Verb, "namespace", requestInfo.Namespace, "name", requestInfo.Name)
resp, err := t.envelopeService.Encrypt(ctx, uid, newKey) resp, err := t.envelopeService.Encrypt(ctx, uid, newKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to encrypt DEK, error: %w", err) return nil, fmt.Errorf("failed to encrypt DEK, error: %w", err)
@ -304,3 +312,10 @@ func ValidateKeyID(keyID string) (ErrCodeKeyID, error) {
} }
return errKeyIDOKCode, nil return errKeyIDOKCode, nil
} }
func getRequestInfoFromContext(ctx context.Context) *genericapirequest.RequestInfo {
if reqInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
return reqInfo
}
return &genericapirequest.RequestInfo{}
}

View File

@ -21,19 +21,23 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/base64" "encoding/base64"
"flag"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/value" "k8s.io/apiserver/pkg/storage/value"
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes" aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1" kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics" "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics"
"k8s.io/component-base/metrics/legacyregistry" "k8s.io/component-base/metrics/legacyregistry"
"k8s.io/component-base/metrics/testutil" "k8s.io/component-base/metrics/testutil"
"k8s.io/klog/v2"
kmsservice "k8s.io/kms/pkg/service" kmsservice "k8s.io/kms/pkg/service"
testingclock "k8s.io/utils/clock/testing" testingclock "k8s.io/utils/clock/testing"
) )
@ -661,3 +665,90 @@ func TestEnvelopeMetrics(t *testing.T) {
}) })
} }
} }
func TestEnvelopeLogging(t *testing.T) {
klog.InitFlags(nil)
flag.Set("v", "6")
flag.Parse()
testCases := []struct {
desc string
ctx context.Context
wantLogs []string
}{
{
desc: "no request info in context",
ctx: testContext(t),
wantLogs: []string{
`"encrypting content using envelope service" uid="UID" key="0123456789" group="" version="" resource="" subresource="" verb="" namespace="" name=""`,
`"decrypting content using envelope service" uid="UID" key="0123456789" group="" version="" resource="" subresource="" verb="" namespace="" name=""`,
},
},
{
desc: "request info in context",
ctx: genericapirequest.WithRequestInfo(testContext(t), &genericapirequest.RequestInfo{
APIGroup: "awesome.bears.com",
APIVersion: "v1",
Resource: "pandas",
Subresource: "status",
Namespace: "kube-system",
Name: "panda",
Verb: "update",
}),
wantLogs: []string{
`"encrypting content using envelope service" uid="UID" key="0123456789" group="awesome.bears.com" version="v1" resource="pandas" subresource="status" verb="update" namespace="kube-system" name="panda"`,
`"decrypting content using envelope service" uid="UID" key="0123456789" group="awesome.bears.com" version="v1" resource="pandas" subresource="status" verb="update" namespace="kube-system" name="panda"`,
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
var buf bytes.Buffer
klog.SetOutput(&buf)
klog.LogToStderr(false)
defer klog.LogToStderr(true)
envelopeService := newTestEnvelopeService()
fakeClock := testingclock.NewFakeClock(time.Now())
envelopeTransformer := newEnvelopeTransformerWithClock(envelopeService, testProviderName,
func(ctx context.Context) (string, error) {
return "1", nil
},
func(ctx context.Context) error {
return nil
},
aestransformer.NewGCMTransformer, 1*time.Second, fakeClock)
dataCtx := value.DefaultContext([]byte(testContextText))
originalText := []byte(testText)
transformedData, err := envelopeTransformer.TransformToStorage(tc.ctx, originalText, dataCtx)
if err != nil {
t.Fatalf("envelopeTransformer: error while transforming data to storage: %v", err)
}
// advance the clock to trigger cache to expire, so we make a decrypt call that will log
fakeClock.Step(2 * time.Second)
_, _, err = envelopeTransformer.TransformFromStorage(tc.ctx, transformedData, dataCtx)
if err != nil {
t.Fatalf("could not decrypt Envelope transformer's encrypted data even once: %v", err)
}
klog.Flush()
klog.SetOutput(&bytes.Buffer{}) // prevent further writes into buf
capturedOutput := buf.String()
// replace the uid with a constant to make the test output stable and assertable
capturedOutput = regexp.MustCompile(`uid="[^"]+"`).ReplaceAllString(capturedOutput, `uid="UID"`)
for _, wantLog := range tc.wantLogs {
if !strings.Contains(capturedOutput, wantLog) {
t.Errorf("expected log %q, got %q", wantLog, capturedOutput)
}
}
})
}
}