Secrets: encryption encryption storage uses versioning (#108036)

* Secrets: delete unused FakeKeeper

* Secrets: encrypted value storage stores versions

* add version to span

* trigger build

* remove ineffectual assignment

* lint

* drop secret_encrypted_value.uid / add name and version columns
This commit is contained in:
Bruno
2025-07-14 09:28:07 -03:00
committed by GitHub
parent afe6cd8a6d
commit baa89f3eac
31 changed files with 454 additions and 302 deletions
@@ -1,12 +1,14 @@
INSERT INTO {{ .Ident "secret_encrypted_value" }} (
{{ .Ident "uid" }},
{{ .Ident "namespace" }},
{{ .Ident "name" }},
{{ .Ident "version" }},
{{ .Ident "encrypted_data" }},
{{ .Ident "created" }},
{{ .Ident "updated" }}
) VALUES (
{{ .Arg .Row.UID }},
{{ .Arg .Row.Namespace }},
{{ .Arg .Row.Name }},
{{ .Arg .Row.Version }},
{{ .Arg .Row.EncryptedData }},
{{ .Arg .Row.Created }},
{{ .Arg .Row.Updated }}
@@ -1,4 +1,6 @@
DELETE FROM {{ .Ident "secret_encrypted_value" }}
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "uid" }} = {{ .Arg .UID }}
WHERE
{{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "name" }} = {{ .Arg .Name }} AND
{{ .Ident "version" }} = {{ .Arg .Version }}
;
@@ -1,11 +1,14 @@
SELECT
{{ .Ident "uid" }},
{{ .Ident "namespace" }},
{{ .Ident "name" }},
{{ .Ident "version" }},
{{ .Ident "encrypted_data" }},
{{ .Ident "created" }},
{{ .Ident "updated" }}
FROM
{{ .Ident "secret_encrypted_value" }}
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "uid" }} = {{ .Arg .UID }}
WHERE
{{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "name" }} = {{ .Arg .Name }} AND
{{ .Ident "version" }} = {{ .Arg .Version }}
;
@@ -3,6 +3,8 @@ UPDATE
SET
{{ .Ident "encrypted_data" }} = {{ .Arg .EncryptedData }},
{{ .Ident "updated" }} = {{ .Arg .Updated }}
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "uid" }} = {{ .Arg .UID }}
WHERE
{{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
{{ .Ident "name" }} = {{ .Arg .Name }} AND
{{ .Ident "version" }} = {{ .Arg .Version }}
;
@@ -3,8 +3,9 @@ package encryption
import "github.com/grafana/grafana/pkg/storage/secret/migrator"
type EncryptedValue struct {
UID string
Namespace string
Name string
Version int64
EncryptedData []byte
Created int64
Updated int64
@@ -6,17 +6,19 @@ import (
"fmt"
"time"
"github.com/google/uuid"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/storage/unified/sql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
)
var (
ErrEncryptedValueNotFound = errors.New("encrypted value not found")
ErrEncryptedValueNotFound = errors.New("encrypted value not found")
ErrEncryptedValueAlreadyExists = errors.New("encrypted value alredy exists")
ErrUnexpectedNumberOfRowsAffected = errors.New("unexpected number of rows modified by query")
)
func ProvideEncryptedValueStorage(
@@ -42,7 +44,7 @@ type encryptedValStorage struct {
tracer trace.Tracer
}
func (s *encryptedValStorage) Create(ctx context.Context, namespace string, encryptedData []byte) (ev *contracts.EncryptedValue, err error) {
func (s *encryptedValStorage) Create(ctx context.Context, namespace, name string, version int64, encryptedData []byte) (ev *contracts.EncryptedValue, err error) {
ctx, span := s.tracer.Start(ctx, "EncryptedValueStorage.Create", trace.WithAttributes(
attribute.String("namespace", namespace),
))
@@ -50,14 +52,20 @@ func (s *encryptedValStorage) Create(ctx context.Context, namespace string, encr
defer func() {
if ev != nil {
span.SetAttributes(attribute.String("uid", ev.UID))
span.SetAttributes(
attribute.String("namespace", ev.Namespace),
attribute.String("name", ev.Name),
attribute.Int64("version", ev.Version),
)
}
}()
createdTime := time.Now().Unix()
encryptedValue := &EncryptedValue{
UID: uuid.New().String(),
Namespace: namespace,
Name: name,
Version: version,
EncryptedData: encryptedData,
Created: createdTime,
Updated: createdTime,
@@ -74,6 +82,9 @@ func (s *encryptedValStorage) Create(ctx context.Context, namespace string, encr
res, err := s.db.ExecContext(ctx, query, req.GetArgs()...)
if err != nil {
if sql.IsRowAlreadyExistsError(err) {
return nil, ErrEncryptedValueAlreadyExists
}
return nil, fmt.Errorf("inserting row: %w", err)
}
@@ -84,25 +95,28 @@ func (s *encryptedValStorage) Create(ctx context.Context, namespace string, encr
}
return &contracts.EncryptedValue{
UID: encryptedValue.UID,
Namespace: encryptedValue.Namespace,
Name: encryptedValue.Name,
Version: encryptedValue.Version,
EncryptedData: encryptedValue.EncryptedData,
Created: encryptedValue.Created,
Updated: encryptedValue.Updated,
}, nil
}
func (s *encryptedValStorage) Update(ctx context.Context, namespace string, uid string, encryptedData []byte) error {
func (s *encryptedValStorage) Update(ctx context.Context, namespace, name string, version int64, encryptedData []byte) error {
ctx, span := s.tracer.Start(ctx, "EncryptedValueStorage.Update", trace.WithAttributes(
attribute.String("uid", uid),
attribute.String("namespace", namespace),
attribute.String("name", name),
attribute.Int64("version", version),
))
defer span.End()
req := updateEncryptedValue{
SQLTemplate: sqltemplate.New(s.dialect),
Namespace: namespace,
UID: uid,
Name: name,
Version: version,
EncryptedData: encryptedData,
Updated: time.Now().Unix(),
}
@@ -120,23 +134,25 @@ func (s *encryptedValStorage) Update(ctx context.Context, namespace string, uid
if rowsAffected, err := res.RowsAffected(); err != nil {
return fmt.Errorf("getting rows affected: %w", err)
} else if rowsAffected != 1 {
return fmt.Errorf("expected 1 row affected, got %d on %s", rowsAffected, namespace)
return fmt.Errorf("expected 1 row affected, got %d on %s: %w", rowsAffected, namespace, ErrUnexpectedNumberOfRowsAffected)
}
return nil
}
func (s *encryptedValStorage) Get(ctx context.Context, namespace string, uid string) (*contracts.EncryptedValue, error) {
func (s *encryptedValStorage) Get(ctx context.Context, namespace, name string, version int64) (*contracts.EncryptedValue, error) {
ctx, span := s.tracer.Start(ctx, "EncryptedValueStorage.Get", trace.WithAttributes(
attribute.String("uid", uid),
attribute.String("namespace", namespace),
attribute.String("name", name),
attribute.Int64("version", version),
))
defer span.End()
req := &readEncryptedValue{
SQLTemplate: sqltemplate.New(s.dialect),
Namespace: namespace,
UID: uid,
Name: name,
Version: version,
}
query, err := sqltemplate.Execute(sqlEncryptedValueRead, req)
if err != nil {
@@ -154,7 +170,7 @@ func (s *encryptedValStorage) Get(ctx context.Context, namespace string, uid str
}
var encryptedValue EncryptedValue
err = rows.Scan(&encryptedValue.UID, &encryptedValue.Namespace, &encryptedValue.EncryptedData, &encryptedValue.Created, &encryptedValue.Updated)
err = rows.Scan(&encryptedValue.Namespace, &encryptedValue.Name, &encryptedValue.Version, &encryptedValue.EncryptedData, &encryptedValue.Created, &encryptedValue.Updated)
if err != nil {
return nil, fmt.Errorf("failed to scan encrypted value row: %w", err)
}
@@ -163,25 +179,28 @@ func (s *encryptedValStorage) Get(ctx context.Context, namespace string, uid str
}
return &contracts.EncryptedValue{
UID: encryptedValue.UID,
Namespace: encryptedValue.Namespace,
Name: encryptedValue.Name,
Version: encryptedValue.Version,
EncryptedData: encryptedValue.EncryptedData,
Created: encryptedValue.Created,
Updated: encryptedValue.Updated,
}, nil
}
func (s *encryptedValStorage) Delete(ctx context.Context, namespace string, uid string) error {
func (s *encryptedValStorage) Delete(ctx context.Context, namespace, name string, version int64) error {
ctx, span := s.tracer.Start(ctx, "EncryptedValueStorage.Delete", trace.WithAttributes(
attribute.String("uid", uid),
attribute.String("namespace", namespace),
attribute.String("name", name),
attribute.Int64("version", version),
))
defer span.End()
req := deleteEncryptedValue{
SQLTemplate: sqltemplate.New(s.dialect),
Namespace: namespace,
UID: uid,
Name: name,
Version: version,
}
query, err := sqltemplate.Execute(sqlEncryptedValueDelete, req)
if err != nil {
@@ -1,46 +1,47 @@
package encryption
package encryption_test
import (
"context"
"errors"
"slices"
"testing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/storage/secret/database"
"github.com/grafana/grafana/pkg/storage/secret/migrator"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/storage/secret/encryption"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
"pgregory.net/rapid"
)
func TestEncryptedValueStoreImpl(t *testing.T) {
// Initialize data key storage with a fake db
testDB := sqlstore.NewTestStore(t, sqlstore.WithMigrator(migrator.New()))
tracer := noop.NewTracerProvider().Tracer("test")
database := database.ProvideDatabase(testDB, tracer)
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
ctx := context.Background()
store, err := ProvideEncryptedValueStorage(database, tracer, features)
require.NoError(t, err)
t.Parallel()
t.Run("creating an encrypted value returns it", func(t *testing.T) {
createdEV, err := store.Create(ctx, "test-namespace", []byte("test-data"))
t.Parallel()
sut := testutils.Setup(t)
createdEV, err := sut.EncryptedValueStorage.Create(t.Context(), "test-namespace", "test-name", 1, []byte("test-data"))
require.NoError(t, err)
require.NotEmpty(t, createdEV.UID)
require.NotEmpty(t, createdEV.Namespace)
require.NotEmpty(t, createdEV.Name)
require.NotEmpty(t, createdEV.Created)
require.NotEmpty(t, createdEV.Updated)
require.NotEmpty(t, createdEV.EncryptedData)
require.Equal(t, "test-namespace", createdEV.Namespace)
require.Equal(t, "test-name", createdEV.Name)
})
t.Run("get an existent encrypted value returns it", func(t *testing.T) {
createdEV, err := store.Create(ctx, "test-namespace", []byte("test-data"))
t.Parallel()
sut := testutils.Setup(t)
createdEV, err := sut.EncryptedValueStorage.Create(t.Context(), "test-namespace", "test-name", 1, []byte("test-data"))
require.NoError(t, err)
obtainedEV, err := store.Get(ctx, "test-namespace", createdEV.UID)
obtainedEV, err := sut.EncryptedValueStorage.Get(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version)
require.NoError(t, err)
require.Equal(t, createdEV.UID, obtainedEV.UID)
require.Equal(t, createdEV.Namespace, obtainedEV.Namespace)
require.Equal(t, createdEV.Name, obtainedEV.Name)
require.Equal(t, createdEV.Created, obtainedEV.Created)
require.Equal(t, createdEV.Updated, obtainedEV.Updated)
require.Equal(t, createdEV.EncryptedData, obtainedEV.EncryptedData)
@@ -48,10 +49,13 @@ func TestEncryptedValueStoreImpl(t *testing.T) {
})
t.Run("get an existent encrypted value with a different namespace returns error", func(t *testing.T) {
createdEV, err := store.Create(ctx, "test-namespace", []byte("test-data"))
t.Parallel()
sut := testutils.Setup(t)
createdEV, err := sut.EncryptedValueStorage.Create(t.Context(), "ns1", "test-name", 1, []byte("test-data"))
require.NoError(t, err)
obtainedEV, err := store.Get(ctx, "other-test-namespace", createdEV.UID)
obtainedEV, err := sut.EncryptedValueStorage.Get(t.Context(), "ns2", createdEV.Name, createdEV.Version)
require.Error(t, err)
require.Equal(t, "encrypted value not found", err.Error())
@@ -59,20 +63,26 @@ func TestEncryptedValueStoreImpl(t *testing.T) {
})
t.Run("get a non existent encrypted value returns error", func(t *testing.T) {
obtainedEV, err := store.Get(ctx, "test-namespace", "test-uid")
t.Parallel()
sut := testutils.Setup(t)
obtainedEV, err := sut.EncryptedValueStorage.Get(t.Context(), "test-namespace", "test-name", 1)
require.Error(t, err)
require.Equal(t, "encrypted value not found", err.Error())
require.Nil(t, obtainedEV)
})
t.Run("updating an existing encrypted value returns no error", func(t *testing.T) {
createdEV, err := store.Create(ctx, "test-namespace", []byte("test-data"))
t.Parallel()
sut := testutils.Setup(t)
createdEV, err := sut.EncryptedValueStorage.Create(t.Context(), "test-namespace", "test-name", 1, []byte("test-data"))
require.NoError(t, err)
err = store.Update(ctx, "test-namespace", createdEV.UID, []byte("test-data-updated"))
err = sut.EncryptedValueStorage.Update(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version, []byte("test-data-updated"))
require.NoError(t, err)
updatedEV, err := store.Get(ctx, "test-namespace", createdEV.UID)
updatedEV, err := sut.EncryptedValueStorage.Get(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version)
require.NoError(t, err)
require.Equal(t, []byte("test-data-updated"), updatedEV.EncryptedData)
@@ -81,27 +91,192 @@ func TestEncryptedValueStoreImpl(t *testing.T) {
})
t.Run("updating a non existing encrypted value returns error", func(t *testing.T) {
err := store.Update(ctx, "test-namespace", "test-uid", []byte("test-data"))
t.Parallel()
sut := testutils.Setup(t)
err := sut.EncryptedValueStorage.Update(t.Context(), "test-namespace", "test-uid", 1, []byte("test-data"))
require.Error(t, err)
})
t.Run("delete an existing encrypted value returns error", func(t *testing.T) {
createdEV, err := store.Create(ctx, "test-namespace", []byte("ttttest-data"))
t.Parallel()
sut := testutils.Setup(t)
createdEV, err := sut.EncryptedValueStorage.Create(t.Context(), "test-namespace", "test-name", 1, []byte("ttttest-data"))
require.NoError(t, err)
obtainedEV, err := store.Get(ctx, "test-namespace", createdEV.UID)
_, err = sut.EncryptedValueStorage.Get(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version)
require.NoError(t, err)
err = store.Delete(ctx, "test-namespace", obtainedEV.UID)
err = sut.EncryptedValueStorage.Delete(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version)
require.NoError(t, err)
obtainedEV, err = store.Get(ctx, "test-namespace", createdEV.UID)
obtainedEV, err := sut.EncryptedValueStorage.Get(t.Context(), createdEV.Namespace, createdEV.Name, createdEV.Version)
require.Error(t, err)
require.Nil(t, obtainedEV)
})
t.Run("delete a non existing encrypted value does not return error", func(t *testing.T) {
err := store.Delete(ctx, "test-namespace", "test-uid")
t.Parallel()
sut := testutils.Setup(t)
err := sut.EncryptedValueStorage.Delete(t.Context(), "test-namespace", "test-name", 1)
require.NoError(t, err)
})
}
func TestStateMachine(t *testing.T) {
t.Parallel()
tt := t
rapid.Check(t, func(t *rapid.T) {
sut := testutils.Setup(tt)
m := newModel()
t.Repeat(map[string]func(*rapid.T){
"create": func(t *rapid.T) {
ns := namespaceGen.Draw(t, "ns")
name := nameGen.Draw(t, "name")
version := versionGen.Draw(t, "version")
plaintext := rapid.String().Draw(t, "plaintext")
_, modelErr := m.create(ns, name, version, []byte(plaintext))
_, err := sut.EncryptedValueStorage.Create(t.Context(), ns, name, version, []byte(plaintext))
if modelErr != nil || err != nil {
require.ErrorIs(t, err, modelErr)
return
}
},
"update": func(t *rapid.T) {
ns := namespaceGen.Draw(t, "ns")
name := nameGen.Draw(t, "name")
version := versionGen.Draw(t, "version")
plaintext := rapid.String().Draw(t, "plaintext")
modelErr := m.update(ns, name, version, []byte(plaintext))
err := sut.EncryptedValueStorage.Update(t.Context(), ns, name, version, []byte(plaintext))
if modelErr != nil || err != nil {
require.ErrorIs(t, err, modelErr)
return
}
},
"get": func(t *rapid.T) {
ns := namespaceGen.Draw(t, "ns")
name := nameGen.Draw(t, "name")
version := versionGen.Draw(t, "version")
modelValue, modelErr := m.get(ns, name, version)
value, err := sut.EncryptedValueStorage.Get(t.Context(), ns, name, version)
if modelErr != nil || err != nil {
require.ErrorIs(t, err, modelErr)
return
}
// Do not compare timestamps because the model doesn't model them.
require.Equal(t, modelValue.Namespace, value.Namespace)
require.Equal(t, modelValue.Name, value.Name)
require.Equal(t, modelValue.EncryptedData, value.EncryptedData)
require.Equal(t, modelValue.Version, value.Version)
},
"delete": func(t *rapid.T) {
ns := namespaceGen.Draw(t, "ns")
name := nameGen.Draw(t, "name")
version := versionGen.Draw(t, "version")
modelErr := m.delete(ns, name, version)
err := sut.EncryptedValueStorage.Delete(t.Context(), ns, name, version)
if modelErr != nil || err != nil {
require.ErrorIs(t, err, modelErr)
return
}
},
})
})
}
var (
namespaceGen = rapid.Custom(func(t *rapid.T) string {
return rapid.SampledFrom([]string{"ns1", "ns2", "ns3", "ns4", "ns5"}).Draw(t, "namespace")
})
nameGen = rapid.Custom(func(t *rapid.T) string {
return rapid.SampledFrom([]string{"name1", "name2", "name3", "name4", "name5"}).Draw(t, "name")
})
versionGen = rapid.Custom(func(t *rapid.T) int64 {
return rapid.Int64Range(1, 5).Draw(t, "version")
})
)
// A simplified model of the encrypted value storage
type model struct {
entries []*entry
}
type entry struct {
namespace string
name string
version int64
encryptedData []byte
}
func newModel() *model {
return &model{}
}
func (m *model) create(namespace, name string, version int64, encryptedData []byte) (*contracts.EncryptedValue, error) {
v, err := m.get(namespace, name, version)
if err != nil && !errors.Is(err, encryption.ErrEncryptedValueNotFound) {
return nil, err
}
// The entry being creted already exists
if v != nil {
return nil, encryption.ErrEncryptedValueAlreadyExists
}
m.entries = append(m.entries, &entry{
namespace: namespace,
name: name,
version: version,
encryptedData: encryptedData,
})
return &contracts.EncryptedValue{
Namespace: namespace,
Name: name,
Version: version,
EncryptedData: encryptedData,
Created: 1,
Updated: 1,
}, nil
}
func (m *model) update(namespace, name string, version int64, encryptedData []byte) error {
for _, v := range m.entries {
if v.namespace == namespace && v.name == name && v.version == version {
v.encryptedData = encryptedData
return nil
}
}
return encryption.ErrUnexpectedNumberOfRowsAffected
}
func (m *model) get(namespace, name string, version int64) (*contracts.EncryptedValue, error) {
for _, v := range m.entries {
if v.namespace == namespace && v.name == name && v.version == version {
return &contracts.EncryptedValue{
Namespace: namespace,
Name: name,
Version: version,
EncryptedData: v.encryptedData,
Created: 1,
Updated: 1,
}, nil
}
}
return nil, encryption.ErrEncryptedValueNotFound
}
func (m *model) delete(namespace, name string, version int64) error {
m.entries = slices.DeleteFunc(m.entries, func(v *entry) bool {
return v.namespace == namespace && v.name == name && v.version == version
})
return nil
}
+6 -3
View File
@@ -55,7 +55,8 @@ func (r createEncryptedValue) Validate() error {
type readEncryptedValue struct {
sqltemplate.SQLTemplate
Namespace string
UID string
Name string
Version int64
}
// Validate is only used if we use `dbutil` from `unifiedstorage`
@@ -67,7 +68,8 @@ func (r readEncryptedValue) Validate() error {
type updateEncryptedValue struct {
sqltemplate.SQLTemplate
Namespace string
UID string
Name string
Version int64
EncryptedData []byte
Updated int64
}
@@ -81,7 +83,8 @@ func (r updateEncryptedValue) Validate() error {
type deleteEncryptedValue struct {
sqltemplate.SQLTemplate
Namespace string
UID string
Name string
Version int64
}
// Validate is only used if we use `dbutil` from `unifiedstorage`
+8 -4
View File
@@ -20,7 +20,8 @@ func TestEncryptedValueQueries(t *testing.T) {
SQLTemplate: mocks.NewTestingSQLTemplate(),
Row: &EncryptedValue{
Namespace: "ns",
UID: "abc123",
Name: "n1",
Version: 1,
EncryptedData: []byte("secret"),
Created: 1234,
Updated: 5678,
@@ -34,7 +35,8 @@ func TestEncryptedValueQueries(t *testing.T) {
Data: &readEncryptedValue{
SQLTemplate: mocks.NewTestingSQLTemplate(),
Namespace: "ns",
UID: "abc123",
Name: "n1",
Version: 1,
},
},
},
@@ -44,7 +46,8 @@ func TestEncryptedValueQueries(t *testing.T) {
Data: &updateEncryptedValue{
SQLTemplate: mocks.NewTestingSQLTemplate(),
Namespace: "ns",
UID: "abc123",
Name: "n1",
Version: 1,
EncryptedData: []byte("secret"),
Updated: 5679,
},
@@ -56,7 +59,8 @@ func TestEncryptedValueQueries(t *testing.T) {
Data: &deleteEncryptedValue{
SQLTemplate: mocks.NewTestingSQLTemplate(),
Namespace: "ns",
UID: "abc123",
Name: "n1",
Version: 1,
},
},
},
@@ -1,12 +1,14 @@
INSERT INTO `secret_encrypted_value` (
`uid`,
`namespace`,
`name`,
`version`,
`encrypted_data`,
`created`,
`updated`
) VALUES (
'abc123',
'ns',
'n1',
1,
'[115 101 99 114 101 116]',
1234,
5678
@@ -1,4 +1,6 @@
DELETE FROM `secret_encrypted_value`
WHERE `namespace` = 'ns' AND
`uid` = 'abc123'
WHERE
`namespace` = 'ns' AND
`name` = 'n1' AND
`version` = 1
;
@@ -1,11 +1,14 @@
SELECT
`uid`,
`namespace`,
`name`,
`version`,
`encrypted_data`,
`created`,
`updated`
FROM
`secret_encrypted_value`
WHERE `namespace` = 'ns' AND
`uid` = 'abc123'
WHERE
`namespace` = 'ns' AND
`name` = 'n1' AND
`version` = 1
;
@@ -3,6 +3,8 @@ UPDATE
SET
`encrypted_data` = '[115 101 99 114 101 116]',
`updated` = 5679
WHERE `namespace` = 'ns' AND
`uid` = 'abc123'
WHERE
`namespace` = 'ns' AND
`name` = 'n1' AND
`version` = 1
;
@@ -1,12 +1,14 @@
INSERT INTO "secret_encrypted_value" (
"uid",
"namespace",
"name",
"version",
"encrypted_data",
"created",
"updated"
) VALUES (
'abc123',
'ns',
'n1',
1,
'[115 101 99 114 101 116]',
1234,
5678
@@ -1,4 +1,6 @@
DELETE FROM "secret_encrypted_value"
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
@@ -1,11 +1,14 @@
SELECT
"uid",
"namespace",
"name",
"version",
"encrypted_data",
"created",
"updated"
FROM
"secret_encrypted_value"
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
@@ -3,6 +3,8 @@ UPDATE
SET
"encrypted_data" = '[115 101 99 114 101 116]',
"updated" = 5679
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
@@ -1,12 +1,14 @@
INSERT INTO "secret_encrypted_value" (
"uid",
"namespace",
"name",
"version",
"encrypted_data",
"created",
"updated"
) VALUES (
'abc123',
'ns',
'n1',
1,
'[115 101 99 114 101 116]',
1234,
5678
@@ -1,4 +1,6 @@
DELETE FROM "secret_encrypted_value"
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
@@ -1,11 +1,14 @@
SELECT
"uid",
"namespace",
"name",
"version",
"encrypted_data",
"created",
"updated"
FROM
"secret_encrypted_value"
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
@@ -3,6 +3,8 @@ UPDATE
SET
"encrypted_data" = '[115 101 99 114 101 116]',
"updated" = 5679
WHERE "namespace" = 'ns' AND
"uid" = 'abc123'
WHERE
"namespace" = 'ns' AND
"name" = 'n1' AND
"version" = 1
;
+4 -4
View File
@@ -94,17 +94,17 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
// The auth token will not necessarily have the permission to read the secure value metadata,
// but we still need to do it to inspect the `decrypters` field, hence the actual `authorize`
// function call happens after this.
sv, err := s.secureValueMetadataStorage.ReadForDecrypt(ctx, namespace, name)
sv, err := s.secureValueMetadataStorage.Read(ctx, namespace, name, contracts.ReadOpts{})
if err != nil {
return "", contracts.ErrDecryptNotFound
}
decrypterIdentity, authorized := s.decryptAuthorizer.Authorize(ctx, name, sv.Decrypters)
decrypterIdentity, authorized := s.decryptAuthorizer.Authorize(ctx, name, sv.Spec.Decrypters)
if !authorized {
return "", contracts.ErrDecryptNotAuthorized
}
keeperConfig, err := s.keeperMetadataStorage.GetKeeperConfig(ctx, namespace.String(), sv.Keeper, contracts.ReadOpts{})
keeperConfig, err := s.keeperMetadataStorage.GetKeeperConfig(ctx, namespace.String(), sv.Spec.Keeper, contracts.ReadOpts{})
if err != nil {
return "", contracts.ErrDecryptFailed
}
@@ -114,7 +114,7 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
return "", contracts.ErrDecryptFailed
}
exposedValue, err := keeper.Expose(ctx, keeperConfig, namespace.String(), contracts.ExternalID(sv.ExternalID))
exposedValue, err := keeper.Expose(ctx, keeperConfig, namespace.String(), name, sv.Status.Version)
if err != nil {
return "", contracts.ErrDecryptFailed
}
@@ -197,6 +197,7 @@ func (s *secureValueMetadataStorage) getLatestVersion(ctx context.Context, names
return &version, nil
}
// TODO: can this method + queries be removed?
func (s *secureValueMetadataStorage) ReadForDecrypt(ctx context.Context, namespace xkube.Namespace, name string) (*contracts.DecryptSecureValue, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.ReadForDecrypt", trace.WithAttributes(
+8 -4
View File
@@ -118,17 +118,21 @@ func (*SecretDB) AddMigration(mg *migrator.Migrator) {
Indices: []*migrator.Index{}, // TODO: add indexes based on the queries we make.
})
tables = append(tables, migrator.Table{
encryptedValueTable := migrator.Table{
Name: TableNameEncryptedValue,
Columns: []*migrator.Column{
{Name: "namespace", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
{Name: "uid", Type: migrator.DB_NVarchar, Length: 36, IsPrimaryKey: true}, // Fixed size of a UUID.
{Name: "name", Type: migrator.DB_NVarchar, Length: 253, Nullable: false},
{Name: "version", Type: migrator.DB_BigInt, Nullable: false},
{Name: "encrypted_data", Type: migrator.DB_Blob, Nullable: false},
{Name: "created", Type: migrator.DB_BigInt, Nullable: false},
{Name: "updated", Type: migrator.DB_BigInt, Nullable: false},
},
Indices: []*migrator.Index{}, // TODO: add indexes based on the queries we make.
})
Indices: []*migrator.Index{
{Cols: []string{"namespace", "name", "version"}, Type: migrator.UniqueIndex},
},
}
tables = append(tables, encryptedValueTable)
// Initialize all tables
for t := range tables {