Skip to content

Commit 07f8481

Browse files
authored
feat: API for encryption at-rest (#610)
**What problem does this PR solve?**: - CAREN API for encryption at-rest ``` topology: variables: - name: clusterConfig value: encryption: providers: - aescbc: {} ``` - [x] Define API schema - [x] Implement mutator handler - [x] unit tests - [x] e2e tests - [x] documentation **Which issue(s) this PR fixes**: Fixes # https://jira.nutanix.com/browse/D2IQ-100555 **How Has This Been Tested?**: <!-- Please describe the tests that you ran to verify your changes. Provide output from the tests and any manual steps needed to replicate the tests. --> **Special notes for your reviewer**: <!-- Use this to provide any additional information to the reviewers. This may include: - Best way to review the PR. - Where the author wants the most review attention on. - etc. -->
1 parent 27b4146 commit 07f8481

12 files changed

+864
-0
lines changed

api/v1alpha1/clusterconfig_types.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ type GenericClusterConfigSpec struct {
200200

201201
// +kubebuilder:validation:Optional
202202
Users []User `json:"users,omitempty"`
203+
204+
// +optional
205+
EncryptionAtRest *EncryptionAtRest `json:"encryptionAtRest,omitempty"`
203206
}
204207

205208
type Image struct {
@@ -279,6 +282,28 @@ type User struct {
279282
Sudo string `json:"sudo,omitempty"`
280283
}
281284

285+
// EncryptionAtRest defines the configuration to enable encryption at REST
286+
// This configuration is used by API server to encrypt data before storing it in ETCD.
287+
// Currently the encryption only enabled for secrets and configmaps.
288+
type EncryptionAtRest struct {
289+
// Encryption providers
290+
// +kubebuilder:default={{aescbc:{}}}
291+
// +kubebuilder:validation:MaxItems=1
292+
// +kubebuilder:validation:Optional
293+
Providers []EncryptionProviders `json:"providers,omitempty"`
294+
}
295+
296+
type EncryptionProviders struct {
297+
// +kubebuilder:validation:Optional
298+
AESCBC *AESConfiguration `json:"aescbc,omitempty"`
299+
// +kubebuilder:validation:Optional
300+
Secretbox *SecretboxConfiguration `json:"secretbox,omitempty"`
301+
}
302+
303+
type AESConfiguration struct{}
304+
305+
type SecretboxConfiguration struct{}
306+
282307
func init() {
283308
SchemeBuilder.Register(
284309
&AWSClusterConfig{},

api/v1alpha1/crds/caren.nutanix.com_awsclusterconfigs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,26 @@ spec:
322322
type: string
323323
type: object
324324
type: object
325+
encryptionAtRest:
326+
description: |-
327+
EncryptionAtRest defines the configuration to enable encryption at REST
328+
This configuration is used by API server to encrypt data before storing it in ETCD.
329+
Currently the encryption only enabled for secrets and configmaps.
330+
properties:
331+
providers:
332+
default:
333+
- aescbc: {}
334+
description: Encryption providers
335+
items:
336+
properties:
337+
aescbc:
338+
type: object
339+
secretbox:
340+
type: object
341+
type: object
342+
maxItems: 1
343+
type: array
344+
type: object
325345
etcd:
326346
properties:
327347
image:

api/v1alpha1/crds/caren.nutanix.com_dockerclusterconfigs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,26 @@ spec:
239239
type: object
240240
docker:
241241
type: object
242+
encryptionAtRest:
243+
description: |-
244+
EncryptionAtRest defines the configuration to enable encryption at REST
245+
This configuration is used by API server to encrypt data before storing it in ETCD.
246+
Currently the encryption only enabled for secrets and configmaps.
247+
properties:
248+
providers:
249+
default:
250+
- aescbc: {}
251+
description: Encryption providers
252+
items:
253+
properties:
254+
aescbc:
255+
type: object
256+
secretbox:
257+
type: object
258+
type: object
259+
maxItems: 1
260+
type: array
261+
type: object
242262
etcd:
243263
properties:
244264
image:

api/v1alpha1/crds/caren.nutanix.com_genericclusterconfigs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,26 @@ spec:
233233
- provider
234234
type: object
235235
type: object
236+
encryptionAtRest:
237+
description: |-
238+
EncryptionAtRest defines the configuration to enable encryption at REST
239+
This configuration is used by API server to encrypt data before storing it in ETCD.
240+
Currently the encryption only enabled for secrets and configmaps.
241+
properties:
242+
providers:
243+
default:
244+
- aescbc: {}
245+
description: Encryption providers
246+
items:
247+
properties:
248+
aescbc:
249+
type: object
250+
secretbox:
251+
type: object
252+
type: object
253+
maxItems: 1
254+
type: array
255+
type: object
236256
etcd:
237257
properties:
238258
image:

api/v1alpha1/crds/caren.nutanix.com_nutanixclusterconfigs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,26 @@ spec:
410410
- machineDetails
411411
type: object
412412
type: object
413+
encryptionAtRest:
414+
description: |-
415+
EncryptionAtRest defines the configuration to enable encryption at REST
416+
This configuration is used by API server to encrypt data before storing it in ETCD.
417+
Currently the encryption only enabled for secrets and configmaps.
418+
properties:
419+
providers:
420+
default:
421+
- aescbc: {}
422+
description: Encryption providers
423+
items:
424+
properties:
425+
aescbc:
426+
type: object
427+
secretbox:
428+
type: object
429+
type: object
430+
maxItems: 1
431+
type: array
432+
type: object
413433
etcd:
414434
properties:
415435
image:

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/pkg/k8s/client/create.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2024 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package client
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
11+
)
12+
13+
func Create(
14+
ctx context.Context,
15+
c ctrlclient.Client,
16+
obj ctrlclient.Object,
17+
opts ...ctrlclient.CreateOption,
18+
) error {
19+
options := []ctrlclient.CreateOption{ctrlclient.FieldOwner(FieldOwner)}
20+
options = append(options, opts...)
21+
err := c.Create(
22+
ctx,
23+
obj,
24+
options...,
25+
)
26+
if err != nil {
27+
return fmt.Errorf("create object failed: %w", err)
28+
}
29+
return nil
30+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2024 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package encryptionatrest
5+
6+
import (
7+
"encoding/base64"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
apiserverv1 "k8s.io/apiserver/pkg/apis/config/v1"
12+
13+
carenv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
)
15+
16+
func Test_encryptionConfigForSecretsAndConfigMaps(t *testing.T) {
17+
testcases := []struct {
18+
name string
19+
providers *carenv1.EncryptionProviders
20+
wantErr error
21+
want *apiserverv1.ResourceConfiguration
22+
}{
23+
{
24+
name: "encryption configuration using all providers",
25+
providers: &carenv1.EncryptionProviders{
26+
AESCBC: &carenv1.AESConfiguration{},
27+
Secretbox: &carenv1.SecretboxConfiguration{},
28+
},
29+
wantErr: nil,
30+
want: &apiserverv1.ResourceConfiguration{
31+
Resources: []string{"secrets", "configmaps"},
32+
Providers: []apiserverv1.ProviderConfiguration{
33+
{
34+
AESCBC: &apiserverv1.AESConfiguration{
35+
Keys: []apiserverv1.Key{
36+
{
37+
Name: "key1",
38+
Secret: base64.StdEncoding.EncodeToString([]byte(testToken)),
39+
},
40+
},
41+
},
42+
Secretbox: &apiserverv1.SecretboxConfiguration{
43+
Keys: []apiserverv1.Key{
44+
{
45+
Name: "key1",
46+
Secret: base64.StdEncoding.EncodeToString([]byte(testToken)),
47+
},
48+
},
49+
},
50+
},
51+
},
52+
},
53+
},
54+
{
55+
name: "encryption configuration using single provider",
56+
providers: &carenv1.EncryptionProviders{
57+
AESCBC: &carenv1.AESConfiguration{},
58+
},
59+
wantErr: nil,
60+
want: &apiserverv1.ResourceConfiguration{
61+
Resources: []string{"secrets", "configmaps"},
62+
Providers: []apiserverv1.ProviderConfiguration{
63+
{
64+
AESCBC: &apiserverv1.AESConfiguration{
65+
Keys: []apiserverv1.Key{
66+
{
67+
Name: "key1",
68+
Secret: base64.StdEncoding.EncodeToString([]byte(testToken)),
69+
},
70+
},
71+
},
72+
},
73+
},
74+
},
75+
},
76+
}
77+
78+
for _, tt := range testcases {
79+
t.Run(tt.name, func(t *testing.T) {
80+
got, gErr := defaultEncryptionConfiguration(
81+
tt.providers,
82+
testTokenGenerator)
83+
assert.Equal(t, tt.wantErr, gErr)
84+
assert.Equal(t, tt.want, got)
85+
})
86+
}
87+
}

0 commit comments

Comments
 (0)