-
Notifications
You must be signed in to change notification settings - Fork 266
NO-JIRA: inject kms sidecar in preflight deployer #2321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| package controllers | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| configv1 "github.com/openshift/api/config/v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| apiserverconfigv1 "k8s.io/apiserver/pkg/apis/apiserver/v1" | ||
| corev1client "k8s.io/client-go/kubernetes/typed/core/v1" | ||
|
|
||
| "github.com/openshift/library-go/pkg/operator/encryption/encryptiondata" | ||
| ) | ||
|
|
||
| // BuildEphemeralPreflightEncryptionConfig builds a one-shot encryption configuration for KMS preflight. | ||
| // It resolves referenced Secrets and ConfigMaps from openshift-config and embeds their data inline. | ||
| func BuildEphemeralPreflightEncryptionConfig( | ||
| ctx context.Context, | ||
| coreClient corev1client.CoreV1Interface, | ||
| pluginConfig configv1.KMSPluginConfig, | ||
| encryptionKeyID string, | ||
| socketEndpoint string, | ||
| kmsCallTimeout time.Duration, | ||
| ) (*encryptiondata.Config, error) { | ||
| providerCfg, err := newKMSProviderConfig(pluginConfig) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var secretData encryptiondata.KMSPluginsReferenceData | ||
| if secretName, expectedKeys, err := providerCfg.referencedSecretName(); err != nil { | ||
| return nil, err | ||
| } else if len(secretName) > 0 { | ||
| refSecret, err := coreClient.Secrets(openshiftConfigNS).Get(ctx, secretName, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get secret %s in %s: %w", secretName, openshiftConfigNS, err) | ||
| } | ||
| for _, key := range expectedKeys { | ||
| value, ok := refSecret.Data[key] | ||
| if !ok { | ||
| return nil, fmt.Errorf("secret %s in %s is missing required key %q", secretName, openshiftConfigNS, key) | ||
| } | ||
| if err := secretData.SetFromRawKey(encryptionKeyID, fmt.Sprintf("%s_%s", secretName, key), value); err != nil { | ||
| return nil, err | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var configMapData encryptiondata.KMSPluginsReferenceData | ||
| if cmName, expectedKeys, err := providerCfg.referencedConfigMapName(); err != nil { | ||
| return nil, err | ||
| } else if len(cmName) > 0 { | ||
| refCM, err := coreClient.ConfigMaps(openshiftConfigNS).Get(ctx, cmName, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to get configmap %s in %s: %w", cmName, openshiftConfigNS, err) | ||
| } | ||
| for _, key := range expectedKeys { | ||
| value, ok := refCM.Data[key] | ||
| if !ok { | ||
| return nil, fmt.Errorf("configmap %s in %s is missing required key %q", cmName, openshiftConfigNS, key) | ||
| } | ||
| if err := configMapData.SetFromRawKey(encryptionKeyID, fmt.Sprintf("%s_%s", cmName, key), []byte(value)); err != nil { | ||
| return nil, err | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return &encryptiondata.Config{ | ||
| Encryption: &apiserverconfigv1.EncryptionConfiguration{ | ||
| TypeMeta: metav1.TypeMeta{ | ||
| Kind: "EncryptionConfiguration", | ||
| APIVersion: "apiserver.config.k8s.io/v1", | ||
| }, | ||
| Resources: []apiserverconfigv1.ResourceConfiguration{{ | ||
| Resources: []string{"secrets"}, | ||
| Providers: []apiserverconfigv1.ProviderConfiguration{{ | ||
| KMS: &apiserverconfigv1.KMSConfiguration{ | ||
| APIVersion: "v2", | ||
| Name: fmt.Sprintf("%s_secrets", encryptionKeyID), | ||
| Endpoint: socketEndpoint, | ||
| Timeout: &metav1.Duration{Duration: kmsCallTimeout}, | ||
| }, | ||
| }, { | ||
| Identity: &apiserverconfigv1.IdentityConfiguration{}, | ||
| }}, | ||
| }}, | ||
| }, | ||
| KMSPlugins: map[string]configv1.KMSPluginConfig{ | ||
| encryptionKeyID: pluginConfig, | ||
| }, | ||
| KMSPluginsSecretData: secretData, | ||
| KMSPluginsConfigMapData: configMapData, | ||
| }, nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,19 +5,30 @@ import ( | |
| "fmt" | ||
| "time" | ||
|
|
||
| configv1 "github.com/openshift/api/config/v1" | ||
| corev1 "k8s.io/api/core/v1" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| corev1client "k8s.io/client-go/kubernetes/typed/core/v1" | ||
|
|
||
| "github.com/openshift/library-go/pkg/operator/encryption/controllers" | ||
| "github.com/openshift/library-go/pkg/operator/encryption/encryptiondata" | ||
| "github.com/openshift/library-go/pkg/operator/encryption/kms/pluginlifecycle" | ||
| "github.com/openshift/library-go/pkg/operator/events" | ||
| "github.com/openshift/library-go/pkg/operator/resource/resourceapply" | ||
| ) | ||
|
|
||
| const ( | ||
| preflightPodName = "kms-preflight" | ||
| preflightPodName = "kms-preflight" | ||
| preflightCheckContainerName = "kms-preflight-check" | ||
| preflightEncryptionKeyID = "1" | ||
| preflightEncryptionConfigSecretName = "kms-preflight-encryption-config" | ||
| kmsSocketEndpoint = "unix:///var/run/kmsplugin/kms.sock" | ||
| ) | ||
|
|
||
| type KMSPreflightDeployer interface { | ||
| // Deploy creates the preflight checker with the given config hash | ||
| Deploy(ctx context.Context, configHash string) error | ||
| // Deploy creates the preflight checker with the given config hash and KMS plugin configuration. | ||
| Deploy(ctx context.Context, configHash string, pluginConfig configv1.KMSPluginConfig) error | ||
| Status(ctx context.Context) (corev1.PodStatus, error) | ||
| // Cleanup cleans up anything previously created. | ||
| Cleanup(ctx context.Context) error | ||
|
|
@@ -31,12 +42,13 @@ type PodPreflightDeployer struct { | |
| // encryption controllers/components actually use. | ||
| // In addition to that running the preflight check is not a very frequent operation. | ||
| coreClient corev1client.CoreV1Interface | ||
| eventRecorder events.Recorder | ||
| operatorImage string | ||
| operatorCommand []string | ||
| kmsCallTimeout time.Duration | ||
| } | ||
|
|
||
| func (d *PodPreflightDeployer) Deploy(ctx context.Context, configHash string) error { | ||
| func (d *PodPreflightDeployer) Deploy(ctx context.Context, configHash string, pluginConfig configv1.KMSPluginConfig) error { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logical flow in that function makes sense to me. I think, we just may need to agree upon the how we align built-in functions. |
||
| pod, err := generatePodTemplate( | ||
| preflightPodName, | ||
| d.namespace, | ||
|
|
@@ -49,7 +61,27 @@ func (d *PodPreflightDeployer) Deploy(ctx context.Context, configHash string) er | |
| return fmt.Errorf("failed to generate preflight pod template: %w", err) | ||
| } | ||
|
|
||
| // TODO(thomas): inject KMS plugin sidecar container into pod.Spec | ||
| encryptionConfig, err := controllers.BuildEphemeralPreflightEncryptionConfig( | ||
| ctx, d.coreClient, pluginConfig, preflightEncryptionKeyID, kmsSocketEndpoint, d.kmsCallTimeout) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to build preflight encryption config: %w", err) | ||
| } | ||
|
|
||
| encryptionConfigSecret, err := ephemeralEncryptionConfigSecret(d.namespace, encryptionConfig) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to build preflight encryption config secret: %w", err) | ||
| } | ||
|
|
||
| if _, _, err := resourceapply.ApplySecret(ctx, d.coreClient, d.eventRecorder, encryptionConfigSecret); err != nil { | ||
| return fmt.Errorf("failed to apply preflight encryption config secret: %w", err) | ||
| } | ||
|
|
||
| err = pluginlifecycle.NewKMSPluginBuilder(). | ||
| FromEncryptionConfig(preflightEncryptionConfigSecretName, encryptionConfig). | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. builder only needs an existed secret name
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's fair, but the pods needs a real secret to run and I need to create it. What should I change here? |
||
| Apply(&pod.Spec, preflightCheckContainerName) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to inject KMS plugin sidecar: %w", err) | ||
| } | ||
|
|
||
| _, err = d.coreClient.Pods(d.namespace).Create(ctx, pod, metav1.CreateOptions{}) | ||
| return err | ||
|
|
@@ -70,21 +102,32 @@ func (d *PodPreflightDeployer) Cleanup(ctx context.Context) error { | |
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return fmt.Errorf("failed to delete pod %s/%s: %w", d.namespace, preflightPodName, err) | ||
| } | ||
|
|
||
| err = d.coreClient.Secrets(d.namespace).Delete(ctx, preflightEncryptionConfigSecretName, metav1.DeleteOptions{}) | ||
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return fmt.Errorf("failed to delete secret %s/%s: %w", d.namespace, preflightEncryptionConfigSecretName, err) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func NewPodPreflightDeployer( | ||
| namespace string, | ||
| coreClient corev1client.CoreV1Interface, | ||
| eventRecorder events.Recorder, | ||
| operatorImage string, | ||
| operatorCommand []string, | ||
| kmsCallTimeout time.Duration, | ||
| ) *PodPreflightDeployer { | ||
| return &PodPreflightDeployer{ | ||
| namespace: namespace, | ||
| coreClient: coreClient, | ||
| eventRecorder: eventRecorder, | ||
| operatorImage: operatorImage, | ||
| operatorCommand: operatorCommand, | ||
| kmsCallTimeout: kmsCallTimeout, | ||
| } | ||
| } | ||
|
|
||
| func ephemeralEncryptionConfigSecret(namespace string, cfg *encryptiondata.Config) (*corev1.Secret, error) { | ||
| return encryptiondata.ToSecret(namespace, preflightEncryptionConfigSecretName, cfg) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and pass encryptionconfig name in the namespace here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the other comment I understood, I've moved the ephemeral builder to the other package. What should I replace this with?