Skip to content

Commit 62a9074

Browse files
committed
YDBOPS-9692 move encryption config to args (#217)
1 parent e7784f9 commit 62a9074

File tree

12 files changed

+307
-93
lines changed

12 files changed

+307
-93
lines changed

api/v1alpha1/configuration.go

-39
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,14 @@ package v1alpha1
22

33
import (
44
"bytes"
5-
"crypto/sha256"
65
"fmt"
7-
"path"
86
"strconv"
97

108
"gopkg.in/yaml.v3"
119

1210
"github.com/ydb-platform/ydb-kubernetes-operator/internal/configuration/schema"
1311
)
1412

15-
const (
16-
DatabaseEncryptionKeyPath = "/opt/ydb/secrets/database_encryption"
17-
DatabaseEncryptionKeyFile = "key"
18-
DatastreamsIAMServiceAccountKeyPath = "/opt/ydb/secrets/datastreams"
19-
DatastreamsIAMServiceAccountKeyFile = "sa_key.json"
20-
)
21-
22-
func hash(text string) string {
23-
h := sha256.New()
24-
h.Write([]byte(text))
25-
return fmt.Sprintf("%x", h.Sum(nil))
26-
}
27-
2813
func generateHosts(cr *Storage) []schema.Host {
2914
var hosts []schema.Host
3015

@@ -61,24 +46,6 @@ func generateHosts(cr *Storage) []schema.Host {
6146
return hosts
6247
}
6348

64-
func generateKeyConfig(cr *Storage, crDB *Database) *schema.KeyConfig {
65-
var keyConfig *schema.KeyConfig
66-
if crDB != nil && crDB.Spec.Encryption != nil && crDB.Spec.Encryption.Enabled {
67-
keyConfig = &schema.KeyConfig{
68-
Keys: []schema.Key{
69-
{
70-
ContainerPath: path.Join(DatabaseEncryptionKeyPath, DatabaseEncryptionKeyFile),
71-
ID: hash(cr.Name),
72-
Pin: crDB.Spec.Encryption.Pin,
73-
Version: 1,
74-
},
75-
},
76-
}
77-
}
78-
79-
return keyConfig
80-
}
81-
8249
func BuildConfiguration(cr *Storage, crDB *Database) ([]byte, error) {
8350
config := make(map[string]interface{})
8451

@@ -113,12 +80,6 @@ func BuildConfiguration(cr *Storage, crDB *Database) ([]byte, error) {
11380
config["hosts"] = hosts
11481
}
11582

116-
// Will be removed by YDBOPS-9692
117-
keyConfig := generateKeyConfig(cr, crDB)
118-
if keyConfig != nil {
119-
config["key_config"] = keyConfig
120-
}
121-
12283
return yaml.Marshal(config)
12384
}
12485

api/v1alpha1/const.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,21 @@ const (
3030
ConfigDir = "/opt/ydb/cfg"
3131
ConfigFileName = "config.yaml"
3232

33+
DatabaseEncryptionKeySecretDir = "encryption"
34+
DatabaseEncryptionKeySecretFile = "key.pem"
35+
DatabaseEncryptionKeyConfigFile = "key.txt"
36+
37+
DatastreamsIAMServiceAccountKeyDir = "datastreams"
38+
DatastreamsIAMServiceAccountKeyFile = "sa_key.json"
39+
3340
BinariesDir = "/opt/ydb/bin"
3441
DaemonBinaryName = "ydbd"
3542

36-
DefaultRootUsername = "root"
37-
DefaultRootPassword = ""
38-
DefaultSignAlgorithm = "RS256"
43+
DefaultRootUsername = "root"
44+
DefaultRootPassword = ""
45+
DefaultDatabaseDomain = "Root"
46+
DefaultDatabaseEncryptionPin = "EmptyPin"
47+
DefaultSignAlgorithm = "RS256"
3948

4049
LabelDeploymentKey = "deployment"
4150
LabelDeploymentValueKubernetes = "kubernetes"

api/v1alpha1/database_webhook.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ import (
1717
. "github.com/ydb-platform/ydb-kubernetes-operator/internal/controllers/constants" //nolint:revive,stylecheck
1818
)
1919

20-
const (
21-
DefaultDatabaseDomain = "Root"
22-
)
23-
2420
// log is for logging in this package.
2521
var databaselog = logf.Log.WithName("database-resource")
2622

@@ -126,6 +122,13 @@ func (r *DatabaseDefaulter) Default(ctx context.Context, obj runtime.Object) err
126122
database.Spec.Encryption = &EncryptionConfig{Enabled: false}
127123
}
128124

125+
if database.Spec.Encryption.Enabled && database.Spec.Encryption.Key == nil {
126+
if database.Spec.Encryption.Pin == nil || len(*database.Spec.Encryption.Pin) == 0 {
127+
encryptionPin := DefaultDatabaseEncryptionPin
128+
database.Spec.Encryption.Pin = &encryptionPin
129+
}
130+
}
131+
129132
if database.Spec.Datastreams == nil {
130133
database.Spec.Datastreams = &DatastreamsConfig{Enabled: false}
131134
}
@@ -149,7 +152,7 @@ func (r *DatabaseDefaulter) Default(ctx context.Context, obj runtime.Object) err
149152
database.Spec.StorageEndpoint = storage.GetStorageEndpointWithProto()
150153
}
151154

152-
if database.Spec.Configuration != "" || (database.Spec.Encryption != nil && database.Spec.Encryption.Enabled) {
155+
if database.Spec.Configuration != "" {
153156
configuration, err := BuildConfiguration(storage, database)
154157
if err != nil {
155158
return err

deploy/ydb-operator/Chart.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ type: application
1515
# This is the chart version. This version number should be incremented each time you make changes
1616
# to the chart and its templates, including the app version.
1717
# Versions are expected to follow Semantic Versioning (https://semver.org/)
18-
version: 0.5.22
18+
version: 0.5.23
1919

2020
# This is the version number of the application being deployed. This version number should be
2121
# incremented each time you make changes to the application. Versions are not expected to
2222
# follow Semantic Versioning. They should reflect the version the application is using.
2323
# It is recommended to use it with quotes.
24-
appVersion: "0.5.22"
24+
appVersion: "0.5.23"

e2e/tests/smoke_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,54 @@ var _ = Describe("Operator smoke test", func() {
866866
)
867867
})
868868

869+
It("Check encryption for Database", func() {
870+
By("create storage...")
871+
Expect(k8sClient.Create(ctx, storageSample)).Should(Succeed())
872+
defer func() {
873+
Expect(k8sClient.Delete(ctx, storageSample)).Should(Succeed())
874+
}()
875+
By("create database...")
876+
databaseSample.Spec.Encryption = &v1alpha1.EncryptionConfig{
877+
Enabled: true,
878+
}
879+
Expect(k8sClient.Create(ctx, databaseSample)).Should(Succeed())
880+
defer func() {
881+
Expect(k8sClient.Delete(ctx, databaseSample)).Should(Succeed())
882+
}()
883+
884+
By("waiting until Storage is ready...")
885+
waitUntilStorageReady(ctx, storageSample.Name, testobjects.YdbNamespace)
886+
887+
By("checking that all the storage pods are running and ready...")
888+
checkPodsRunningAndReady(ctx, "ydb-cluster", "kind-storage", storageSample.Spec.Nodes)
889+
890+
By("waiting until database is ready...")
891+
waitUntilDatabaseReady(ctx, databaseSample.Name, testobjects.YdbNamespace)
892+
893+
By("checking that all the database pods are running and ready...")
894+
checkPodsRunningAndReady(ctx, "ydb-cluster", "kind-database", databaseSample.Spec.Nodes)
895+
896+
database := v1alpha1.Database{}
897+
Expect(k8sClient.Get(ctx, types.NamespacedName{
898+
Name: databaseSample.Name,
899+
Namespace: testobjects.YdbNamespace,
900+
}, &database)).Should(Succeed())
901+
storageEndpoint := database.Spec.StorageEndpoint
902+
903+
databasePods := corev1.PodList{}
904+
Expect(k8sClient.List(ctx, &databasePods,
905+
client.InNamespace(testobjects.YdbNamespace),
906+
client.MatchingLabels{"ydb-cluster": "kind-database"}),
907+
).Should(Succeed())
908+
podName := databasePods.Items[0].Name
909+
910+
By("bring YDB CLI inside ydb database pod...")
911+
bringYdbCliToPod(podName, testobjects.YdbNamespace)
912+
913+
By("execute simple query inside ydb database pod...")
914+
executeSimpleQuery(podName, testobjects.YdbNamespace, storageEndpoint)
915+
})
916+
869917
AfterEach(func() {
870918
Expect(uninstallOperatorWithHelm(testobjects.YdbNamespace)).Should(BeTrue())
871919
Expect(k8sClient.Delete(ctx, &namespace)).Should(Succeed())

internal/controllers/database/controller_test.go

+74
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package database_test
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"path/filepath"
8+
"reflect"
79
"strings"
810
"testing"
911

@@ -142,5 +144,77 @@ var _ = Describe("Database controller medium tests", func() {
142144
}
143145
}
144146
})
147+
148+
By("Check encryption for Database...")
149+
foundDatabase := v1alpha1.Database{}
150+
Expect(k8sClient.Get(ctx, types.NamespacedName{
151+
Name: databaseSample.Name,
152+
Namespace: testobjects.YdbNamespace,
153+
}, &foundDatabase))
154+
155+
By("Update Database and enable encryption...")
156+
foundDatabase.Spec.Encryption = &v1alpha1.EncryptionConfig{Enabled: true}
157+
Expect(k8sClient.Update(ctx, &foundDatabase)).Should(Succeed())
158+
159+
By("Check that encryption secret was created...")
160+
encryptionSecret := corev1.Secret{}
161+
Eventually(func() error {
162+
return k8sClient.Get(ctx, types.NamespacedName{
163+
Name: databaseSample.Name,
164+
Namespace: testobjects.YdbNamespace,
165+
}, &encryptionSecret)
166+
}, test.Timeout, test.Interval).ShouldNot(HaveOccurred())
167+
encryptionData := encryptionSecret.Data
168+
169+
By("Check that arg `--key-file` was added to StatefulSet...")
170+
databaseStatefulSet = appsv1.StatefulSet{}
171+
Eventually(func() error {
172+
Expect(k8sClient.List(ctx,
173+
&foundStatefulSets,
174+
client.InNamespace(testobjects.YdbNamespace),
175+
)).ShouldNot(HaveOccurred())
176+
for idx, statefulSet := range foundStatefulSets.Items {
177+
if statefulSet.Name == testobjects.DatabaseName {
178+
databaseStatefulSet = foundStatefulSets.Items[idx]
179+
break
180+
}
181+
}
182+
podContainerArgs := databaseStatefulSet.Spec.Template.Spec.Containers[0].Args
183+
encryptionKeyConfigPath := fmt.Sprintf("%s/%s", v1alpha1.ConfigDir, v1alpha1.DatabaseEncryptionKeyConfigFile)
184+
for idx, arg := range podContainerArgs {
185+
if arg == "--key-file" {
186+
if podContainerArgs[idx+1] == encryptionKeyConfigPath {
187+
return nil
188+
}
189+
return fmt.Errorf(
190+
"Found arg `--key-file=%s` for encryption does not match with expected path: %s",
191+
podContainerArgs[idx+1],
192+
encryptionKeyConfigPath,
193+
)
194+
}
195+
}
196+
return errors.New("Failed to find arg `--key-file` for encryption in StatefulSet")
197+
}, test.Timeout, test.Interval).ShouldNot(HaveOccurred())
198+
199+
By("Update Database encryption pin...")
200+
Expect(k8sClient.Get(ctx, types.NamespacedName{
201+
Name: databaseSample.Name,
202+
Namespace: testobjects.YdbNamespace,
203+
}, &foundDatabase))
204+
pin := "Ignore"
205+
foundDatabase.Spec.Encryption = &v1alpha1.EncryptionConfig{
206+
Enabled: true,
207+
Pin: &pin,
208+
}
209+
Expect(k8sClient.Update(ctx, &foundDatabase)).Should(Succeed())
210+
211+
By("Check that Secret for encryption was not changed...")
212+
Consistently(func(g Gomega) bool {
213+
g.Expect(k8sClient.Get(ctx, types.NamespacedName{
214+
Name: databaseSample.Name,
215+
Namespace: testobjects.YdbNamespace,
216+
}, &encryptionSecret))
217+
return reflect.DeepEqual(encryptionData, encryptionSecret.Data)
218+
}, test.Timeout, test.Interval).Should(BeTrue())
145219
})
146220
})

internal/controllers/database/sync.go

+7
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,13 @@ func shouldIgnoreDatabaseChange(database *resources.DatabaseBuilder) resources.I
370370
return true
371371
}
372372
}
373+
374+
if sec, ok := oldObj.(*corev1.Secret); ok {
375+
// Do not update already existing secret data for encryption
376+
if (len(sec.StringData) > 0) || (len(sec.Data) > 0) {
377+
return true
378+
}
379+
}
373380
return false
374381
}
375382
}

internal/resources/configmap.go

+64-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
package resources
22

33
import (
4+
"bytes"
45
"errors"
6+
"fmt"
7+
"html/template"
58

69
v1 "k8s.io/api/core/v1"
710
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
811
"sigs.k8s.io/controller-runtime/pkg/client"
12+
13+
api "github.com/ydb-platform/ydb-kubernetes-operator/api/v1alpha1"
14+
"github.com/ydb-platform/ydb-kubernetes-operator/internal/configuration/schema"
915
)
1016

17+
const keyConfigTmpl = `Keys {
18+
ContainerPath: "{{ .ContainerPath }}"
19+
Pin: "{{ .Pin }}"
20+
Id: "{{ .ID }}"
21+
Version: {{ .Version }}
22+
}`
23+
1124
type ConfigMapBuilder struct {
1225
client.Object
1326

1427
Name string
15-
Data map[string]string
1628
Labels map[string]string
29+
30+
Data map[string]string
31+
}
32+
33+
type EncryptionConfigBuilder struct {
34+
client.Object
35+
36+
Name string
37+
Labels map[string]string
38+
39+
KeyConfig schema.KeyConfig
1740
}
1841

1942
func (b *ConfigMapBuilder) Build(obj client.Object) error {
@@ -23,13 +46,43 @@ func (b *ConfigMapBuilder) Build(obj client.Object) error {
2346
}
2447

2548
if cm.ObjectMeta.Name == "" {
26-
cm.ObjectMeta.Name = b.GetName()
49+
cm.ObjectMeta.Name = b.Name
2750
}
2851
cm.ObjectMeta.Namespace = b.GetNamespace()
2952

53+
cm.Labels = b.Labels
54+
3055
cm.Data = b.Data
56+
57+
return nil
58+
}
59+
60+
func (b *EncryptionConfigBuilder) Build(obj client.Object) error {
61+
cm, ok := obj.(*v1.ConfigMap)
62+
if !ok {
63+
return errors.New("failed to cast to ConfigMap object")
64+
}
65+
66+
if cm.ObjectMeta.Name == "" {
67+
cm.ObjectMeta.Name = b.Name
68+
}
69+
cm.ObjectMeta.Namespace = b.GetNamespace()
70+
3171
cm.Labels = b.Labels
3272

73+
t, err := template.New("keyConfig").Parse(keyConfigTmpl)
74+
if err != nil {
75+
return fmt.Errorf("failed to parse keyConfig template: %w", err)
76+
}
77+
78+
var buf bytes.Buffer
79+
err = t.Execute(&buf, b.KeyConfig.Keys[0])
80+
if err != nil {
81+
return fmt.Errorf("failed to execute keyConfig template: %w", err)
82+
}
83+
84+
cm.Data = map[string]string{api.DatabaseEncryptionKeyConfigFile: buf.String()}
85+
3386
return nil
3487
}
3588

@@ -41,3 +94,12 @@ func (b *ConfigMapBuilder) Placeholder(cr client.Object) client.Object {
4194
},
4295
}
4396
}
97+
98+
func (b *EncryptionConfigBuilder) Placeholder(cr client.Object) client.Object {
99+
return &v1.ConfigMap{
100+
ObjectMeta: metav1.ObjectMeta{
101+
Name: b.Name,
102+
Namespace: cr.GetNamespace(),
103+
},
104+
}
105+
}

0 commit comments

Comments
 (0)