Skip to content

Commit b115e9e

Browse files
committed
kms: Add unsupported config for vault kms plugin log level
Also wires definition of unsupported config overrides for kms sidecars
1 parent 5637f8b commit b115e9e

6 files changed

Lines changed: 217 additions & 12 deletions

File tree

pkg/operator/encryption/kms/pluginlifecycle/sidecar.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ type sidecarProvider interface {
4040

4141
// newSidecarProvider creates a provider-specific sidecarProvider for the given keyID and plugin configuration,
4242
// wiring in credentials via the credentialResolver.
43-
func newSidecarProvider(keyID string, udsPath string, pluginConfig configv1.KMSPluginConfig, creds *credentialResolver) (sidecarProvider, error) {
43+
func newSidecarProvider(keyID string, udsPath string, pluginConfig configv1.KMSPluginConfig, creds *credentialResolver, unsupportedConfig []byte) (sidecarProvider, error) {
4444
switch pluginConfig.Type {
4545
case configv1.VaultKMSProvider:
46-
return newVaultSidecarProvider("vault-kms-plugin", keyID, udsPath, pluginConfig.Vault, creds)
46+
return newVaultSidecarProvider("vault-kms-plugin", keyID, udsPath, pluginConfig.Vault, creds, unsupportedConfig)
4747
default:
4848
return nil, fmt.Errorf("unsupported KMS plugin configuration")
4949
}
@@ -57,11 +57,11 @@ func newSidecarProvider(keyID string, udsPath string, pluginConfig configv1.KMSP
5757
//
5858
// It is a no-op when the KMSEncryption feature gate is not enabled or the encryption-config secret does not exist.
5959
// The secretClient should be uncached to avoid injecting sidecars based on a stale encryption configuration.
60-
func AddKMSPluginSidecarToStaticPodSpec(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess) error {
60+
func AddKMSPluginSidecarToStaticPodSpec(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess, unsupportedConfig []byte) error {
6161
// The static pod revision controller copies secret data to disk under resourcesDir/secrets/<secretName>/.
6262
credentialsDir := filepath.Join(resourcesDir, "secrets", encryptionConfigSecretName)
6363

64-
sidecarNames, err := addKMSPluginSidecars(ctx, podSpec, containerName, encryptionConfigNamespace, encryptionConfigSecretName, secretClient, featureGateAccessor, credentialsDir)
64+
sidecarNames, err := addKMSPluginSidecars(ctx, podSpec, containerName, encryptionConfigNamespace, encryptionConfigSecretName, secretClient, featureGateAccessor, credentialsDir, unsupportedConfig)
6565
if err != nil {
6666
return err
6767
}
@@ -89,8 +89,8 @@ func AddKMSPluginSidecarToStaticPodSpec(ctx context.Context, podSpec *corev1.Pod
8989
//
9090
// It is a no-op when the KMSEncryption feature gate is not enabled or the encryption-config secret does not exist.
9191
// The secretClient should be uncached to avoid injecting sidecars based on a stale encryption configuration.
92-
func AddKMSPluginSidecarToPodSpec(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess) error {
93-
sidecarNames, err := addKMSPluginSidecars(ctx, podSpec, containerName, encryptionConfigNamespace, encryptionConfigSecretName, secretClient, featureGateAccessor, credentialsMountPath)
92+
func AddKMSPluginSidecarToPodSpec(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess, unsupportedConfig []byte) error {
93+
sidecarNames, err := addKMSPluginSidecars(ctx, podSpec, containerName, encryptionConfigNamespace, encryptionConfigSecretName, secretClient, featureGateAccessor, credentialsMountPath, unsupportedConfig)
9494
if err != nil {
9595
return err
9696
}
@@ -118,7 +118,7 @@ func AddKMSPluginSidecarToPodSpec(ctx context.Context, podSpec *corev1.PodSpec,
118118

119119
// addKMSPluginSidecars contains the shared logic for discovering KMS plugins and injecting sidecar containers.
120120
// It returns the names of the sidecar containers that were injected, so callers can add deployment-mode-specific volume mounts.
121-
func addKMSPluginSidecars(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess, credentialsDir string) ([]string, error) {
121+
func addKMSPluginSidecars(ctx context.Context, podSpec *corev1.PodSpec, containerName string, encryptionConfigNamespace string, encryptionConfigSecretName string, secretClient corev1client.SecretsGetter, featureGateAccessor featuregates.FeatureGateAccess, credentialsDir string, unsupportedConfig []byte) ([]string, error) {
122122
if podSpec == nil {
123123
return nil, fmt.Errorf("pod spec cannot be nil")
124124
}
@@ -183,7 +183,7 @@ func addKMSPluginSidecars(ctx context.Context, podSpec *corev1.PodSpec, containe
183183
keyID: keyID,
184184
}
185185

186-
provider, err := newSidecarProvider(keyID, udsPath, pluginConfig, creds)
186+
provider, err := newSidecarProvider(keyID, udsPath, pluginConfig, creds, unsupportedConfig)
187187
if err != nil {
188188
return nil, fmt.Errorf("failed to create a sidecar provider for keyID %s: %w", keyID, err)
189189
}

pkg/operator/encryption/kms/pluginlifecycle/sidecar_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ func TestAddKMSPluginSidecarToPodSpec(t *testing.T) {
504504

505505
for _, tt := range tests {
506506
t.Run(tt.name, func(t *testing.T) {
507-
err := AddKMSPluginSidecarToPodSpec(context.Background(), tt.actualPodSpec, "kube-apiserver", "openshift-kube-apiserver", "encryption-config", tt.secretClient, tt.featureGateAccessor)
507+
err := AddKMSPluginSidecarToPodSpec(context.Background(), tt.actualPodSpec, "kube-apiserver", "openshift-kube-apiserver", "encryption-config", tt.secretClient, tt.featureGateAccessor, nil)
508508
if tt.wantErr != "" {
509509
require.ErrorContains(t, err, tt.wantErr)
510510
return
@@ -529,7 +529,7 @@ func TestAddKMSPluginSidecarToPodSpecIdempotency(t *testing.T) {
529529

530530
call := func() {
531531
t.Helper()
532-
err := AddKMSPluginSidecarToPodSpec(context.Background(), podSpec, "kube-apiserver", "openshift-kube-apiserver", "encryption-config", sc, fga)
532+
err := AddKMSPluginSidecarToPodSpec(context.Background(), podSpec, "kube-apiserver", "openshift-kube-apiserver", "encryption-config", sc, fga, nil)
533533
require.NoError(t, err)
534534
}
535535

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package pluginlifecycle
2+
3+
import (
4+
"encoding/json"
5+
6+
"k8s.io/klog/v2"
7+
8+
kyaml "k8s.io/apimachinery/pkg/util/yaml"
9+
)
10+
11+
type unsupportedKMSConfig struct {
12+
Encryption struct {
13+
KMS struct {
14+
Vault struct {
15+
LogLevel string `json:"logLevel"`
16+
} `json:"vault"`
17+
} `json:"kms"`
18+
} `json:"encryption"`
19+
}
20+
21+
func parseUnsupportedKMSConfig(raw []byte) (unsupportedKMSConfig, error) {
22+
if len(raw) == 0 {
23+
return unsupportedKMSConfig{}, nil
24+
}
25+
26+
jsonRaw, err := kyaml.ToJSON(raw)
27+
if err != nil {
28+
klog.Warning(err)
29+
return unsupportedKMSConfig{}, err
30+
}
31+
32+
config := unsupportedKMSConfig{}
33+
if err := json.Unmarshal(jsonRaw, &config); err != nil {
34+
return unsupportedKMSConfig{}, nil
35+
}
36+
37+
return config, nil
38+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package pluginlifecycle
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestParseUnsupportedKMSConfig(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
raw []byte
13+
expectedLogLevel string
14+
expectError bool
15+
}{
16+
{
17+
name: "nil input returns empty config",
18+
raw: nil,
19+
expectedLogLevel: "",
20+
},
21+
{
22+
name: "empty input returns empty config",
23+
raw: []byte{},
24+
expectedLogLevel: "",
25+
},
26+
{
27+
name: "JSON with log level",
28+
raw: []byte(`{"encryption":{"kms":{"vault":{"logLevel":"debug-extended"}}}}`),
29+
expectedLogLevel: "debug-extended",
30+
},
31+
{
32+
name: "YAML with log level",
33+
raw: []byte("encryption:\n kms:\n vault:\n logLevel: trace\n"),
34+
expectedLogLevel: "trace",
35+
},
36+
{
37+
name: "unrelated fields are ignored",
38+
raw: []byte(`{"encryption":{"reason":"test"}}`),
39+
expectedLogLevel: "",
40+
},
41+
{
42+
name: "malformed JSON is handled gracefully",
43+
raw: []byte(`{not json`),
44+
expectedLogLevel: "",
45+
},
46+
{
47+
name: "unparsable input returns error",
48+
raw: []byte{0x00, 0x01, 0x02},
49+
expectError: true,
50+
},
51+
}
52+
53+
for _, tt := range tests {
54+
t.Run(tt.name, func(t *testing.T) {
55+
config, err := parseUnsupportedKMSConfig(tt.raw)
56+
if tt.expectError {
57+
require.Error(t, err)
58+
return
59+
}
60+
require.NoError(t, err)
61+
require.Equal(t, tt.expectedLogLevel, config.Encryption.KMS.Vault.LogLevel)
62+
})
63+
}
64+
}

pkg/operator/encryption/kms/pluginlifecycle/vault.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// newVaultSidecarProvider creates a Vault sidecar provider from the given KMS plugin data.
1313
// It assumes the input data has been already been validated.
14-
func newVaultSidecarProvider(name, keyID, udsPath string, vaultConfig configv1.VaultKMSPluginConfig, creds *credentialResolver) (*vault, error) {
14+
func newVaultSidecarProvider(name, keyID, udsPath string, vaultConfig configv1.VaultKMSPluginConfig, creds *credentialResolver, unsupportedConfig []byte) (*vault, error) {
1515
secretName := vaultConfig.Authentication.AppRole.Secret.Name
1616
if secretName == "" {
1717
return nil, fmt.Errorf("vault AppRole authentication secret name cannot be empty")
@@ -35,13 +35,19 @@ func newVaultSidecarProvider(name, keyID, udsPath string, vaultConfig configv1.V
3535
return nil, fmt.Errorf("secret ID path cannot be empty")
3636
}
3737

38+
kmsConfig, err := parseUnsupportedKMSConfig(unsupportedConfig)
39+
if err != nil {
40+
return nil, err
41+
}
42+
3843
return &vault{
3944
name: name,
4045
keyID: keyID,
4146
udsPath: udsPath,
4247
config: vaultConfig,
4348
roleID: roleID,
4449
secretIDPath: secretIDPath,
50+
logLevel: kmsConfig.Encryption.KMS.Vault.LogLevel,
4551
}, nil
4652
}
4753

@@ -53,6 +59,7 @@ type vault struct {
5359
config configv1.VaultKMSPluginConfig
5460
roleID string
5561
secretIDPath string
62+
logLevel string
5663
}
5764

5865
// Name returns the sidecar name appended by the key id.
@@ -77,6 +84,9 @@ func (v *vault) BuildSidecarContainer() (corev1.Container, error) {
7784
if v.config.VaultNamespace != "" {
7885
args = append(args, fmt.Sprintf("-vault-namespace=%s", v.config.VaultNamespace))
7986
}
87+
if v.logLevel != "" {
88+
args = append(args, fmt.Sprintf("-log-level=%s", v.logLevel))
89+
}
8090

8191
// Temporary workarounds. These should go away as we progress with the feature.
8292
args = append(args,

pkg/operator/encryption/kms/pluginlifecycle/vault_test.go

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestVaultSidecarProvider_BuildSidecarContainer(t *testing.T) {
2929
containerName string
3030
keyID string
3131
udsPath string
32+
unsupportedConfig []byte
3233
inputContainers []corev1.Container
3334
expectedContainers []corev1.Container
3435
expectErr string
@@ -196,6 +197,98 @@ func TestVaultSidecarProvider_BuildSidecarContainer(t *testing.T) {
196197
udsPath: "unix:///var/run/kmsplugin/kms-555.sock",
197198
expectErr: "vault AppRole authentication secret name cannot be empty",
198199
},
200+
{
201+
name: "nil unsupported config omits log level",
202+
vaultConfig: configv1.VaultKMSPluginConfig{
203+
KMSPluginImage: "quay.io/test/vault:v2",
204+
VaultAddress: "https://vault.example.com:8200",
205+
TransitKey: "my-key",
206+
TransitMount: "transit",
207+
Authentication: configv1.VaultAuthentication{
208+
AppRole: configv1.VaultAppRoleAuthentication{
209+
Secret: configv1.VaultSecretReference{Name: "vault-approle"},
210+
},
211+
},
212+
},
213+
secretData: newVaultAppRoleSecretData(t, "test-role-id", "test-secret-id"),
214+
credentialsDir: "/var/run/secrets/kms-plugin",
215+
containerName: "kms-plugin",
216+
keyID: "555",
217+
udsPath: "unix:///var/run/kmsplugin/kms-555.sock",
218+
unsupportedConfig: nil,
219+
expectedContainers: []corev1.Container{
220+
{
221+
Name: "kms-plugin-555",
222+
Image: "quay.io/test/vault:v2",
223+
Args: []string{
224+
"-listen-address=unix:///var/run/kmsplugin/kms-555.sock",
225+
"-vault-address=https://vault.example.com:8200",
226+
"-transit-mount=transit",
227+
"-transit-key=my-key",
228+
"-approle-role-id=test-role-id",
229+
"-approle-secret-id-path=/var/run/secrets/kms-plugin/kms-plugin-secret-vault-approle_secret-id-555",
230+
"-tls-skip-verify=true",
231+
"-metrics-port=0",
232+
},
233+
ImagePullPolicy: corev1.PullIfNotPresent,
234+
RestartPolicy: ptr.To(corev1.ContainerRestartPolicyAlways),
235+
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
236+
Resources: corev1.ResourceRequirements{
237+
Requests: corev1.ResourceList{
238+
corev1.ResourceMemory: resource.MustParse("64Mi"),
239+
corev1.ResourceCPU: resource.MustParse("10m"),
240+
},
241+
},
242+
},
243+
},
244+
},
245+
{
246+
name: "log level override appended to args",
247+
vaultConfig: configv1.VaultKMSPluginConfig{
248+
KMSPluginImage: "quay.io/test/vault:v2",
249+
VaultAddress: "https://vault.example.com:8200",
250+
TransitKey: "my-key",
251+
TransitMount: "transit",
252+
Authentication: configv1.VaultAuthentication{
253+
AppRole: configv1.VaultAppRoleAuthentication{
254+
Secret: configv1.VaultSecretReference{Name: "vault-approle"},
255+
},
256+
},
257+
},
258+
secretData: newVaultAppRoleSecretData(t, "test-role-id", "test-secret-id"),
259+
credentialsDir: "/var/run/secrets/kms-plugin",
260+
containerName: "kms-plugin",
261+
keyID: "555",
262+
udsPath: "unix:///var/run/kmsplugin/kms-555.sock",
263+
unsupportedConfig: []byte(`{"encryption":{"kms":{"vault":{"logLevel":"debug-extended"}}}}`),
264+
inputContainers: nil,
265+
expectedContainers: []corev1.Container{
266+
{
267+
Name: "kms-plugin-555",
268+
Image: "quay.io/test/vault:v2",
269+
Args: []string{
270+
"-listen-address=unix:///var/run/kmsplugin/kms-555.sock",
271+
"-vault-address=https://vault.example.com:8200",
272+
"-transit-mount=transit",
273+
"-transit-key=my-key",
274+
"-approle-role-id=test-role-id",
275+
"-approle-secret-id-path=/var/run/secrets/kms-plugin/kms-plugin-secret-vault-approle_secret-id-555",
276+
"-log-level=debug-extended",
277+
"-tls-skip-verify=true",
278+
"-metrics-port=0",
279+
},
280+
ImagePullPolicy: corev1.PullIfNotPresent,
281+
RestartPolicy: ptr.To(corev1.ContainerRestartPolicyAlways),
282+
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
283+
Resources: corev1.ResourceRequirements{
284+
Requests: corev1.ResourceList{
285+
corev1.ResourceMemory: resource.MustParse("64Mi"),
286+
corev1.ResourceCPU: resource.MustParse("10m"),
287+
},
288+
},
289+
},
290+
},
291+
},
199292
}
200293

201294
for _, tt := range tests {
@@ -212,7 +305,7 @@ func TestVaultSidecarProvider_BuildSidecarContainer(t *testing.T) {
212305
keyID: tt.keyID,
213306
}
214307

215-
provider, err := newVaultSidecarProvider(tt.containerName, tt.keyID, tt.udsPath, tt.vaultConfig, creds)
308+
provider, err := newVaultSidecarProvider(tt.containerName, tt.keyID, tt.udsPath, tt.vaultConfig, creds, tt.unsupportedConfig)
216309
if tt.expectErr != "" {
217310
require.EqualError(t, err, tt.expectErr)
218311
return

0 commit comments

Comments
 (0)