Skip to content

Commit a099f40

Browse files
committed
RKE2ControlPlane: v1beta1 support EncryptionConfig
This commit adds RKE2ControlPlane.spec.serverConfig.secretsEncryption property. This allows to specify provider type (aescbc or secretbox) and load encryption key from a secret. Signed-off-by: Dinar Valeev <[email protected]>
1 parent 8ad2334 commit a099f40

File tree

7 files changed

+224
-0
lines changed

7 files changed

+224
-0
lines changed

controlplane/api/v1alpha1/zz_generated.conversion.go

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

controlplane/api/v1beta1/rke2controlplane_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ type RKE2ServerConfig struct {
211211
//+optional
212212
Etcd EtcdConfig `json:"etcd,omitempty"`
213213

214+
// SecretsEncrytion defines encryption at rest configuration
215+
//+optional
216+
SecretsEncryptionProvider *SecretsEncryption `json:"secretsEncryption,omitempty"`
217+
214218
// KubeAPIServer defines optional custom configuration of the Kube API Server.
215219
//+optional
216220
KubeAPIServer *bootstrapv1.ComponentConfig `json:"kubeAPIServer,omitempty"`
@@ -402,6 +406,15 @@ type EtcdS3 struct {
402406
Folder string `json:"folder,omitempty"`
403407
}
404408

409+
// SecretsEncryption defines encryption configuration.
410+
type SecretsEncryption struct {
411+
// EncyptionKey secret reference
412+
EncryptionKeySecret *corev1.ObjectReference `json:"encryptionKeySecret,omitempty"`
413+
// Encryption provider
414+
// +kubebuilder:validation:Enum=aescbc;secretbox
415+
Provider string `json:"provider,omitempty"`
416+
}
417+
405418
// CNI defines the Cni options for deploying RKE2.
406419
type CNI string
407420

controlplane/api/v1beta1/zz_generated.deepcopy.go

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

controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanes.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,6 +2557,59 @@ spec:
25572557
pauseImage:
25582558
description: PauseImage Override image to use for pause.
25592559
type: string
2560+
secretsEncryption:
2561+
description: SecretsEncrytion defines encryption at rest configuration
2562+
properties:
2563+
encryptionKeySecret:
2564+
description: EncyptionKey secret reference
2565+
properties:
2566+
apiVersion:
2567+
description: API version of the referent.
2568+
type: string
2569+
fieldPath:
2570+
description: |-
2571+
If referring to a piece of an object instead of an entire object, this string
2572+
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
2573+
For example, if the object reference is to a container within a pod, this would take on a value like:
2574+
"spec.containers{name}" (where "name" refers to the name of the container that triggered
2575+
the event) or if no container name is specified "spec.containers[2]" (container with
2576+
index 2 in this pod). This syntax is chosen only to have some well-defined way of
2577+
referencing a part of an object.
2578+
type: string
2579+
kind:
2580+
description: |-
2581+
Kind of the referent.
2582+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
2583+
type: string
2584+
name:
2585+
description: |-
2586+
Name of the referent.
2587+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
2588+
type: string
2589+
namespace:
2590+
description: |-
2591+
Namespace of the referent.
2592+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
2593+
type: string
2594+
resourceVersion:
2595+
description: |-
2596+
Specific resourceVersion to which this reference is made, if any.
2597+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
2598+
type: string
2599+
uid:
2600+
description: |-
2601+
UID of the referent.
2602+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
2603+
type: string
2604+
type: object
2605+
x-kubernetes-map-type: atomic
2606+
provider:
2607+
description: Encryption provider
2608+
enum:
2609+
- aescbc
2610+
- secretbox
2611+
type: string
2612+
type: object
25602613
serviceNodePortRange:
25612614
description: 'ServiceNodePortRange is the port range to reserve
25622615
for services with NodePort visibility (default: "30000-32767").'

controlplane/config/crd/bases/controlplane.cluster.x-k8s.io_rke2controlplanetemplates.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,60 @@ spec:
14141414
pauseImage:
14151415
description: PauseImage Override image to use for pause.
14161416
type: string
1417+
secretsEncryption:
1418+
description: SecretsEncrytion defines encryption at rest
1419+
configuration
1420+
properties:
1421+
encryptionKeySecret:
1422+
description: EncyptionKey secret reference
1423+
properties:
1424+
apiVersion:
1425+
description: API version of the referent.
1426+
type: string
1427+
fieldPath:
1428+
description: |-
1429+
If referring to a piece of an object instead of an entire object, this string
1430+
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
1431+
For example, if the object reference is to a container within a pod, this would take on a value like:
1432+
"spec.containers{name}" (where "name" refers to the name of the container that triggered
1433+
the event) or if no container name is specified "spec.containers[2]" (container with
1434+
index 2 in this pod). This syntax is chosen only to have some well-defined way of
1435+
referencing a part of an object.
1436+
type: string
1437+
kind:
1438+
description: |-
1439+
Kind of the referent.
1440+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
1441+
type: string
1442+
name:
1443+
description: |-
1444+
Name of the referent.
1445+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
1446+
type: string
1447+
namespace:
1448+
description: |-
1449+
Namespace of the referent.
1450+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
1451+
type: string
1452+
resourceVersion:
1453+
description: |-
1454+
Specific resourceVersion to which this reference is made, if any.
1455+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
1456+
type: string
1457+
uid:
1458+
description: |-
1459+
UID of the referent.
1460+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
1461+
type: string
1462+
type: object
1463+
x-kubernetes-map-type: atomic
1464+
provider:
1465+
description: Encryption provider
1466+
enum:
1467+
- aescbc
1468+
- secretbox
1469+
type: string
1470+
type: object
14171471
serviceNodePortRange:
14181472
description: 'ServiceNodePortRange is the port range to
14191473
reserve for services with NodePort visibility (default:

pkg/rke2/config.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ const (
4141
// DefaultRKE2CloudProviderConfigLocation is the default location for the RKE2 cloud provider config file.
4242
DefaultRKE2CloudProviderConfigLocation = "/etc/rancher/rke2/cloud-provider-config"
4343

44+
// EncryptionConfigurationLocation a location where rke2 looks up for encryption config.
45+
EncryptionConfigurationLocation = "/var/lib/rancher/rke2/server/cred/encryption-config.json"
46+
4447
// DefaultRKE2JoinPort is the default port used for joining nodes to the cluster. It is open on the control plane nodes.
4548
DefaultRKE2JoinPort = 9345
4649

@@ -83,6 +86,8 @@ fi
8386
# Applying kernel parameters
8487
sysctl -p /etc/sysctl.d/90-rke2-cis.conf
8588
`
89+
//nolint:lll //intentionally compacted to a single line
90+
encptionConfigTemplate = `{"kind":"EncryptionConfiguration","apiVersion":"apiserver.config.k8s.io/v1","resources":[{"resources":["secrets"],"providers":[{"%s":{"keys":[{"name":"enckey","secret":"%s"}]}},{"identity":{}}]}]}`
8691
)
8792

8893
// ServerConfig is a struct that contains the information needed to generate a RKE2 server config.
@@ -137,6 +142,7 @@ type ServerConfig struct {
137142
DatastoreCAFile string `yaml:"datastore-cafile,omitempty"`
138143
DatastoreCertFile string `yaml:"datastore-certfile,omitempty"`
139144
DatastoreKeyFile string `yaml:"datastore-keyfile,omitempty"`
145+
SecretsEncryptionProvider string `yaml:"secrets-encryption-provider"`
140146

141147
// We don't expose these fields in the API
142148
ClusterCIDR string `yaml:"cluster-cidr,omitempty"`
@@ -359,6 +365,30 @@ func newRKE2ServerConfig(opts ServerConfigOpts) (*ServerConfig, []bootstrapv1.Fi
359365
rke2ServerConfig.KubeSchedulerExtraEnv = componentMapToSlice(extraEnv, opts.ServerConfig.KubeScheduler.ExtraEnv)
360366
}
361367

368+
if opts.ServerConfig.SecretsEncryptionProvider != nil {
369+
rke2ServerConfig.SecretsEncryptionProvider = opts.ServerConfig.SecretsEncryptionProvider.Provider
370+
encryptionSecret := &corev1.Secret{}
371+
372+
if err := opts.Client.Get(opts.Ctx, types.NamespacedName{
373+
Name: opts.ServerConfig.SecretsEncryptionProvider.EncryptionKeySecret.Name,
374+
Namespace: opts.ServerConfig.SecretsEncryptionProvider.EncryptionKeySecret.Namespace,
375+
}, encryptionSecret); err != nil {
376+
return nil, nil, fmt.Errorf("failed to get encryptionKey secret: %w", err)
377+
}
378+
379+
key, ok := encryptionSecret.Data["encryptionKey"]
380+
if !ok {
381+
return nil, nil, errors.New("encryptionKey secret missing 'encryptionKey' key")
382+
}
383+
384+
files = append(files, bootstrapv1.File{
385+
Path: EncryptionConfigurationLocation,
386+
Content: generateEncryptionConfig(opts.ServerConfig.SecretsEncryptionProvider.Provider, string(key)),
387+
Owner: consts.DefaultFileOwner,
388+
Permissions: "0400",
389+
})
390+
}
391+
362392
if opts.ServerConfig.KubeControllerManager != nil {
363393
rke2ServerConfig.KubeControllerManagerArgs = opts.ServerConfig.KubeControllerManager.ExtraArgs
364394
rke2ServerConfig.KubeControllerManagerImage = opts.ServerConfig.KubeControllerManager.OverrideImage
@@ -727,3 +757,7 @@ func componentMapToSlice(componentType componentType, input map[string]string) [
727757

728758
return result
729759
}
760+
761+
func generateEncryptionConfig(provider, key string) string {
762+
return fmt.Sprintf(encptionConfigTemplate, provider, key)
763+
}

pkg/rke2/config_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ import (
3131
"github.com/rancher/cluster-api-provider-rke2/pkg/consts"
3232
)
3333

34+
const (
35+
expEncryptionConfig = `{"kind":"EncryptionConfiguration","apiVersion":"apiserver.config.k8s.io/v1","resources":[{"resources":["secrets"],"providers":[{"secretbox":{"keys":[{"name":"enckey","secret":"test_encryption_key"}]}},{"identity":{}}]}]}`
36+
)
37+
3438
var _ = Describe("RKE2ServerConfig", func() {
3539
var opts *ServerConfigOpts
3640

@@ -274,6 +278,46 @@ var _ = Describe("RKE2ServerConfig", func() {
274278
})
275279
})
276280

281+
var _ = Describe("RKE2 Server Config with secretbox encryption", func() {
282+
var opts *ServerConfigOpts
283+
284+
BeforeEach(func() {
285+
286+
opts = &ServerConfigOpts{
287+
ControlPlaneEndpoint: "testendpoint",
288+
Ctx: context.Background(),
289+
Client: fake.NewClientBuilder().WithObjects(
290+
&corev1.Secret{
291+
ObjectMeta: metav1.ObjectMeta{
292+
Name: "encryption-key",
293+
Namespace: "test",
294+
},
295+
Data: map[string][]byte{
296+
"encryption-key": []byte("test_encryption_key"),
297+
},
298+
},
299+
).Build(),
300+
ServerConfig: controlplanev1.RKE2ServerConfig{
301+
SecretsEncryptionProvider: &controlplanev1.SecretsEncryption{
302+
Provider: "secretbox",
303+
EncryptionKeySecret: &corev1.ObjectReference{
304+
Name: "encryption-key",
305+
Namespace: "test",
306+
},
307+
},
308+
},
309+
}
310+
It("should succefully generate a server config with secretbox key provider", func() {
311+
rke2ServerConfig, files, err := GenerateInitControlPlaneConfig(*opts)
312+
Expect(err).ToNot(HaveOccurred())
313+
314+
Expect(rke2ServerConfig.SecretsEncryptionProvider).To(Equal("secretbox"))
315+
Expect(files[5].Content).To(Equal(expEncryptionConfig))
316+
317+
})
318+
})
319+
})
320+
277321
var _ = Describe("RKE2 Agent Config", func() {
278322
var opts *AgentConfigOpts
279323

0 commit comments

Comments
 (0)