From a3a274af46802b90d081e1511fb72ac6ee1a0caf Mon Sep 17 00:00:00 2001 From: Gabe Alford Date: Tue, 31 Oct 2023 16:23:39 -0600 Subject: [PATCH] feat: update falconadmission resource - create generic configmap reconcilation for multiple configmaps - reconcile registry certificates - update deployment on port changes - ensure pushing images is done to proper registries w/settings --- controllers/admission/configmap.go | 88 ++++++++++ .../admission/falconadmission_controller.go | 78 ++++----- controllers/admission/image_push.go | 87 +++++++--- controllers/admission/rbac.go | 12 +- controllers/falcon_container/image_push.go | 13 +- controllers/falcon_container/injector.go | 5 + .../openshift/resources/admission/README.md | 2 +- docs/resources/admission/README.md | 2 +- docs/src/resources/admission.md.tmpl | 2 +- internal/controller/assets/deployment.go | 151 ++++++++++-------- internal/controller/assets/deployment_test.go | 47 +----- 11 files changed, 297 insertions(+), 190 deletions(-) create mode 100644 controllers/admission/configmap.go diff --git a/controllers/admission/configmap.go b/controllers/admission/configmap.go new file mode 100644 index 00000000..94a4c99c --- /dev/null +++ b/controllers/admission/configmap.go @@ -0,0 +1,88 @@ +package controllers + +import ( + "context" + "fmt" + "reflect" + + falconv1alpha1 "github.com/crowdstrike/falcon-operator/api/falcon/v1alpha1" + "github.com/crowdstrike/falcon-operator/internal/controller/assets" + k8sutils "github.com/crowdstrike/falcon-operator/internal/controller/common" + "github.com/crowdstrike/falcon-operator/pkg/common" + "github.com/crowdstrike/falcon-operator/pkg/falcon_api" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + types "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *FalconAdmissionReconciler) reconcileRegistryCABundleConfigMap(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (bool, error) { + return r.reconcileGenericConfigMap(falconAdmission.Name+"-registry-certs", r.newCABundleConfigMap, ctx, req, log, falconAdmission) +} + +func (r *FalconAdmissionReconciler) reconcileConfigMap(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (bool, error) { + return r.reconcileGenericConfigMap(falconAdmission.Name+"-config", r.newConfigMap, ctx, req, log, falconAdmission) +} + +func (r *FalconAdmissionReconciler) reconcileGenericConfigMap(name string, genFunc func(context.Context, string, *falconv1alpha1.FalconAdmission) (*corev1.ConfigMap, error), ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (bool, error) { + cm, err := genFunc(ctx, name, falconAdmission) + if err != nil { + return false, err + } + + existingCM := &corev1.ConfigMap{} + err = r.Get(ctx, types.NamespacedName{Name: name, Namespace: falconAdmission.Spec.InstallNamespace}, existingCM) + if err != nil && apierrors.IsNotFound(err) { + err = k8sutils.Create(r.Client, r.Scheme, ctx, req, log, falconAdmission, &falconAdmission.Status, cm) + if err != nil { + return false, err + } + + return false, nil + } else if err != nil { + log.Error(err, "Failed to get FalconAdmission ConfigMap") + return false, err + } + + if !reflect.DeepEqual(cm.Data, existingCM.Data) { + existingCM.Data = cm.Data + if err := k8sutils.Update(r.Client, ctx, req, log, falconAdmission, &falconAdmission.Status, existingCM); err != nil { + return false, err + } + return true, nil + } + + return false, nil + +} + +func (r *FalconAdmissionReconciler) newCABundleConfigMap(ctx context.Context, name string, falconAdmission *falconv1alpha1.FalconAdmission) (*corev1.ConfigMap, error) { + data := make(map[string]string) + if falconAdmission.Spec.Registry.TLS.CACertificate != "" { + data["tls.crt"] = string(common.DecodeBase64Interface(falconAdmission.Spec.Registry.TLS.CACertificate)) + + return assets.SensorConfigMap(name, falconAdmission.Spec.InstallNamespace, common.FalconSidecarSensor, data), nil + } + return &corev1.ConfigMap{}, fmt.Errorf("unable to determine contents of Registry TLS CACertificate attribute") +} + +func (r *FalconAdmissionReconciler) newConfigMap(ctx context.Context, name string, falconAdmission *falconv1alpha1.FalconAdmission) (*corev1.ConfigMap, error) { + var err error + data := common.MakeSensorEnvMap(falconAdmission.Spec.Falcon) + + cid := "" + if falconAdmission.Spec.Falcon.CID != nil { + cid = *falconAdmission.Spec.Falcon.CID + } + + if cid == "" && falconAdmission.Spec.FalconAPI != nil { + cid, err = falcon_api.FalconCID(ctx, falconAdmission.Spec.FalconAPI.CID, falconAdmission.Spec.FalconAPI.ApiConfig()) + if err != nil { + return &corev1.ConfigMap{}, err + } + } + data["FALCONCTL_OPT_CID"] = cid + + return assets.SensorConfigMap(name, falconAdmission.Spec.InstallNamespace, common.FalconAdmissionController, data), nil +} diff --git a/controllers/admission/falconadmission_controller.go b/controllers/admission/falconadmission_controller.go index 5188bd8a..880f2e31 100644 --- a/controllers/admission/falconadmission_controller.go +++ b/controllers/admission/falconadmission_controller.go @@ -13,7 +13,6 @@ import ( k8sutils "github.com/crowdstrike/falcon-operator/internal/controller/common" "github.com/crowdstrike/falcon-operator/pkg/aws" "github.com/crowdstrike/falcon-operator/pkg/common" - "github.com/crowdstrike/falcon-operator/pkg/falcon_api" "github.com/crowdstrike/falcon-operator/pkg/registry/pulltoken" "github.com/crowdstrike/falcon-operator/pkg/tls" "github.com/crowdstrike/falcon-operator/version" @@ -142,11 +141,11 @@ func (r *FalconAdmissionReconciler) Reconcile(ctx context.Context, req ctrl.Requ // Image being set will override other image based settings if falconAdmission.Spec.Image != "" { if _, err := r.setImageTag(ctx, falconAdmission); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to set Falcon Container Image version: %v", err) + return ctrl.Result{}, fmt.Errorf("failed to set Falcon Admission Image version: %v", err) } } else if os.Getenv("RELATED_IMAGE_ADMISSION_CONTROLLER") != "" && falconAdmission.Spec.FalconAPI == nil { if _, err := r.setImageTag(ctx, falconAdmission); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to set Falcon Container Image version: %v", err) + return ctrl.Result{}, fmt.Errorf("failed to set Falcon Admission Image version: %v", err) } } else { switch falconAdmission.Spec.Registry.Type { @@ -164,9 +163,19 @@ func (r *FalconAdmissionReconciler) Reconcile(ctx context.Context, req ctrl.Requ } } + // Create a CA Bundle ConfigMap if CACertificate attribute is set; overridden by the presence of a CACertificateConfigMap value + if falconAdmission.Spec.Registry.TLS.CACertificateConfigMap == "" && falconAdmission.Spec.Registry.TLS.CACertificate != "" { + if _, err := r.reconcileRegistryCABundleConfigMap(ctx, req, log, falconAdmission); err != nil { + if err != nil { + return ctrl.Result{}, err + } + + } + } + if r.imageMirroringEnabled(falconAdmission) { if err := r.PushImage(ctx, log, falconAdmission); err != nil { - return ctrl.Result{}, fmt.Errorf("cannot refresh Falcon Container image: %v", err) + return ctrl.Result{}, fmt.Errorf("cannot refresh Falcon Admission image: %v", err) } } else { updated, err = r.verifyCrowdStrike(ctx, log, falconAdmission) @@ -174,7 +183,7 @@ func (r *FalconAdmissionReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } if err != nil { - log.Error(err, "Failed to verify CrowdStrike Container Image Registry access") + log.Error(err, "Failed to verify CrowdStrike Admission Image Registry access") time.Sleep(time.Second * 5) return ctrl.Result{RequeueAfter: 5 * time.Second}, err } @@ -341,50 +350,6 @@ func (r *FalconAdmissionReconciler) reconcileTLSSecret(ctx context.Context, req return &corev1.Secret{}, nil } -func (r *FalconAdmissionReconciler) reconcileConfigMap(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (bool, error) { - existingCM := &corev1.ConfigMap{} - var err error - data := common.MakeSensorEnvMap(falconAdmission.Spec.Falcon) - name := falconAdmission.Name + "-config" - - cid := "" - if falconAdmission.Spec.Falcon.CID != nil { - cid = *falconAdmission.Spec.Falcon.CID - } - - if cid == "" && falconAdmission.Spec.FalconAPI != nil { - cid, err = falcon_api.FalconCID(ctx, falconAdmission.Spec.FalconAPI.CID, falconAdmission.Spec.FalconAPI.ApiConfig()) - if err != nil { - return false, err - } - } - data["FALCONCTL_OPT_CID"] = cid - - cm := assets.SensorConfigMap(name, falconAdmission.Spec.InstallNamespace, common.FalconAdmissionController, data) - err = r.Get(ctx, types.NamespacedName{Name: name, Namespace: falconAdmission.Spec.InstallNamespace}, existingCM) - if err != nil && apierrors.IsNotFound(err) { - err = k8sutils.Create(r.Client, r.Scheme, ctx, req, log, falconAdmission, &falconAdmission.Status, cm) - if err != nil { - return false, err - } - - return false, nil - } else if err != nil { - log.Error(err, "Failed to get FalconAdmission ConfigMap") - return false, err - } - - if !reflect.DeepEqual(cm.Data, existingCM.Data) { - existingCM.Data = cm.Data - if err := k8sutils.Update(r.Client, ctx, req, log, falconAdmission, &falconAdmission.Status, existingCM); err != nil { - return false, err - } - return true, nil - } - - return false, nil -} - func (r *FalconAdmissionReconciler) reconcileService(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (bool, error) { existingService := &corev1.Service{} selector := map[string]string{common.FalconComponentKey: common.FalconAdmissionController} @@ -576,6 +541,16 @@ func (r *FalconAdmissionReconciler) reconcileAdmissionDeployment(ctx context.Con existingDeployment.Spec.Template.Spec.Containers[i].Resources = containers.Resources updated = true } + + if !reflect.DeepEqual(containers.LivenessProbe.ProbeHandler.HTTPGet.Port, existingDeployment.Spec.Template.Spec.Containers[i].LivenessProbe.ProbeHandler.HTTPGet.Port) { + existingDeployment.Spec.Template.Spec.Containers[i].LivenessProbe.ProbeHandler.HTTPGet.Port = containers.LivenessProbe.ProbeHandler.HTTPGet.Port + updated = true + } + + if !reflect.DeepEqual(containers.StartupProbe.ProbeHandler.HTTPGet.Port, existingDeployment.Spec.Template.Spec.Containers[i].StartupProbe.ProbeHandler.HTTPGet.Port) { + existingDeployment.Spec.Template.Spec.Containers[i].StartupProbe.ProbeHandler.HTTPGet.Port = containers.StartupProbe.ProbeHandler.HTTPGet.Port + updated = true + } } if updated { @@ -622,10 +597,11 @@ func (r *FalconAdmissionReconciler) reconcileRegistrySecret(ctx context.Context, func (r *FalconAdmissionReconciler) reconcileImageStream(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) (*imagev1.ImageStream, error) { const imageStreamName = "falcon-admission-controller" - imageStream := assets.ImageStream(imageStreamName, falconAdmission.Spec.InstallNamespace, common.FalconAdmissionController) + namespace := r.imageNamespace(falconAdmission) + imageStream := assets.ImageStream(imageStreamName, namespace, common.FalconAdmissionController) existingImageStream := &imagev1.ImageStream{} - err := r.Get(ctx, types.NamespacedName{Name: imageStreamName, Namespace: falconAdmission.Spec.InstallNamespace}, existingImageStream) + err := r.Get(ctx, types.NamespacedName{Name: imageStreamName, Namespace: namespace}, existingImageStream) if err != nil && apierrors.IsNotFound(err) { err = k8sutils.Create(r.Client, r.Scheme, ctx, req, log, falconAdmission, &falconAdmission.Status, imageStream) if err != nil { diff --git a/controllers/admission/image_push.go b/controllers/admission/image_push.go index f1b3a5aa..17d926f3 100644 --- a/controllers/admission/image_push.go +++ b/controllers/admission/image_push.go @@ -4,18 +4,23 @@ import ( "context" "fmt" "os" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" falconv1alpha1 "github.com/crowdstrike/falcon-operator/api/falcon/v1alpha1" "github.com/crowdstrike/falcon-operator/internal/controller/image" + "github.com/crowdstrike/falcon-operator/pkg/aws" "github.com/crowdstrike/falcon-operator/pkg/common" + "github.com/crowdstrike/falcon-operator/pkg/gcp" "github.com/crowdstrike/falcon-operator/pkg/k8s_utils" "github.com/crowdstrike/falcon-operator/pkg/registry/auth" "github.com/crowdstrike/falcon-operator/pkg/registry/falcon_registry" "github.com/crowdstrike/falcon-operator/pkg/registry/pushtoken" "github.com/crowdstrike/gofalcon/falcon" "github.com/go-logr/logr" + imagev1 "github.com/openshift/api/image/v1" "k8s.io/apimachinery/pkg/api/meta" ) @@ -25,6 +30,11 @@ func (r *FalconAdmissionReconciler) PushImage(ctx context.Context, log logr.Logg return err } + // If we have version locking enabled (as it is by default), use the already configured version if present + if r.versionLock(falconAdmission) { + return nil + } + pushAuth, err := r.pushAuth(ctx, falconAdmission) if err != nil { return err @@ -34,22 +44,17 @@ func (r *FalconAdmissionReconciler) PushImage(ctx context.Context, log logr.Logg image := image.NewImageRefresher(ctx, log, r.falconApiConfig(ctx, falconAdmission), pushAuth, falconAdmission.Spec.Registry.TLS.InsecureSkipVerify) version := falconAdmission.Spec.Version - // If we have version locking enabled (as it is by default), use the already configured version if present - if r.versionLock(falconAdmission) { - return nil - } - tag, err := image.Refresh(registryUri, common.SensorTypeKac, version) if err != nil { - return fmt.Errorf("Cannot push Falcon Container Image: %v", err) + return fmt.Errorf("Cannot push Falcon Admission Image: %v", err) } - log.Info("Falcon Container Image pushed successfully", "Image.Tag", tag) + log.Info("Falcon Admission Controller Image pushed successfully", "Image.Tag", tag) falconAdmission.Status.Sensor = &tag imageUri, err := r.imageUri(ctx, falconAdmission) if err != nil { - return fmt.Errorf("Cannot identify Falcon Container Image: %v", err) + return fmt.Errorf("Cannot identify Falcon Admission Image: %v", err) } meta.SetStatusCondition(&falconAdmission.Status.Conditions, metav1.Condition{ @@ -77,7 +82,7 @@ func (r *FalconAdmissionReconciler) verifyCrowdStrike(ctx context.Context, log l return false, nil } - log.Info("Skipping push of Falcon Container image to local registry. Remote CrowdStrike registry will be used.") + log.Info("Skipping push of Falcon Admission image to local registry. Remote CrowdStrike registry will be used.") meta.SetStatusCondition(&falconAdmission.Status.Conditions, metav1.Condition{ Status: metav1.ConditionTrue, Reason: falconv1alpha1.ReasonDiscovered, @@ -90,12 +95,49 @@ func (r *FalconAdmissionReconciler) verifyCrowdStrike(ctx context.Context, log l } func (r *FalconAdmissionReconciler) registryUri(ctx context.Context, falconAdmission *falconv1alpha1.FalconAdmission) (string, error) { - cloud, err := falconAdmission.Spec.FalconAPI.FalconCloud(ctx) - if err != nil { - return "", err - } + switch falconAdmission.Spec.Registry.Type { + case falconv1alpha1.RegistryTypeOpenshift: + imageStream := &imagev1.ImageStream{} + err := r.Get(ctx, types.NamespacedName{Name: "falcon-admission-controller", Namespace: r.imageNamespace(falconAdmission)}, imageStream) + if err != nil { + return "", err + } - return falcon_registry.SensorImageURI(cloud, common.SensorTypeKac), nil + if imageStream.Status.DockerImageRepository == "" { + return "", fmt.Errorf("Unable to find route to OpenShift on-cluster registry. Please verify that OpenShift on-cluster registry is up and running.") + } + + return imageStream.Status.DockerImageRepository, nil + case falconv1alpha1.RegistryTypeGCR: + projectId, err := gcp.GetProjectID() + if err != nil { + return "", fmt.Errorf("Cannot get GCP Project ID: %v", err) + } + + return "gcr.io/" + projectId + "/falcon-kac", nil + case falconv1alpha1.RegistryTypeECR: + repo, err := aws.UpsertECRRepo(ctx, "falcon-kac") + if err != nil { + return "", fmt.Errorf("Cannot get target docker URI for ECR repository: %v", err) + } + + return *repo.RepositoryUri, nil + case falconv1alpha1.RegistryTypeACR: + if falconAdmission.Spec.Registry.AcrName == nil { + return "", fmt.Errorf("Cannot push Falcon Image locally to ACR. acr_name was not specified") + } + + return fmt.Sprintf("%s.azurecr.io/falcon-kac", *falconAdmission.Spec.Registry.AcrName), nil + case falconv1alpha1.RegistryTypeCrowdStrike: + cloud, err := falconAdmission.Spec.FalconAPI.FalconCloud(ctx) + if err != nil { + return "", err + } + + return falcon_registry.SensorImageURI(cloud, common.SensorTypeKac), nil + default: + return "", fmt.Errorf("Unrecognized registry type: %s", falconAdmission.Spec.Registry.Type) + } } func (r *FalconAdmissionReconciler) imageUri(ctx context.Context, falconAdmission *falconv1alpha1.FalconAdmission) (string, error) { @@ -115,7 +157,7 @@ func (r *FalconAdmissionReconciler) imageUri(ctx context.Context, falconAdmissio imageTag, err := r.setImageTag(ctx, falconAdmission) if err != nil { - return "", fmt.Errorf("failed to set Falcon Container Image version: %v", err) + return "", fmt.Errorf("failed to set Falcon Admission Image version: %v", err) } return fmt.Sprintf("%s:%s", registryUri, imageTag), nil @@ -126,7 +168,7 @@ func (r *FalconAdmissionReconciler) getImageTag(ctx context.Context, falconAdmis return *falconAdmission.Status.Sensor, nil } - return "", fmt.Errorf("Unable to get falcon container version") + return "", fmt.Errorf("Unable to get falcon admission container image version") } func (r *FalconAdmissionReconciler) setImageTag(ctx context.Context, falconAdmission *falconv1alpha1.FalconAdmission) (string, error) { @@ -167,10 +209,19 @@ func (r *FalconAdmissionReconciler) setImageTag(ctx context.Context, falconAdmis func (r *FalconAdmissionReconciler) pushAuth(ctx context.Context, falconAdmission *falconv1alpha1.FalconAdmission) (auth.Credentials, error) { return pushtoken.GetCredentials(ctx, falconAdmission.Spec.Registry.Type, - k8s_utils.QuerySecretsInNamespace(r.Client, falconAdmission.Spec.InstallNamespace), + k8s_utils.QuerySecretsInNamespace(r.Client, r.imageNamespace(falconAdmission)), ) } +func (r *FalconAdmissionReconciler) imageNamespace(falconAdmission *falconv1alpha1.FalconAdmission) string { + if falconAdmission.Spec.Registry.Type == falconv1alpha1.RegistryTypeOpenshift { + // Within OpenShift, ImageStreams are separated by namespaces. The "openshift" namespace + // is shared and images pushed there can be referenced by deployments in other namespaces + return "openshift" + } + return falconAdmission.Spec.InstallNamespace +} + func (r *FalconAdmissionReconciler) falconApiConfig(ctx context.Context, falconAdmission *falconv1alpha1.FalconAdmission) *falcon.ApiConfig { cfg := falconAdmission.Spec.FalconAPI.ApiConfig() cfg.Context = ctx @@ -183,5 +234,5 @@ func (r *FalconAdmissionReconciler) imageMirroringEnabled(falconAdmission *falco } func (r *FalconAdmissionReconciler) versionLock(falconAdmission *falconv1alpha1.FalconAdmission) bool { - return falconAdmission.Spec.Version != nil && falconAdmission.Status.Sensor != nil && *falconAdmission.Spec.Version == *falconAdmission.Status.Sensor + return (falconAdmission.Spec.Version != nil && falconAdmission.Status.Sensor != nil && strings.Contains(*falconAdmission.Status.Sensor, *falconAdmission.Spec.Version)) || (falconAdmission.Spec.Version == nil && falconAdmission.Status.Sensor != nil) } diff --git a/controllers/admission/rbac.go b/controllers/admission/rbac.go index a5adbdc5..b48a62de 100644 --- a/controllers/admission/rbac.go +++ b/controllers/admission/rbac.go @@ -23,12 +23,20 @@ const ( func (r *FalconAdmissionReconciler) reconcileServiceAccount(ctx context.Context, req ctrl.Request, log logr.Logger, falconAdmission *falconv1alpha1.FalconAdmission) error { update := false + existingServiceAccount := &corev1.ServiceAccount{} + + imagePullSecrets := []corev1.LocalObjectReference{{Name: common.FalconPullSecretName}} + for _, secret := range falconAdmission.Spec.AdmissionConfig.ImagePullSecrets { + if secret.Name != common.FalconPullSecretName { + imagePullSecrets = append(imagePullSecrets, corev1.LocalObjectReference{Name: secret.Name}) + } + } + serviceAccount := assets.ServiceAccount(common.AdmissionServiceAccountName, falconAdmission.Spec.InstallNamespace, common.FalconAdmissionController, falconAdmission.Spec.AdmissionConfig.ServiceAccount.Annotations, - falconAdmission.Spec.AdmissionConfig.ImagePullSecrets) - existingServiceAccount := &corev1.ServiceAccount{} + imagePullSecrets) err := r.Get(ctx, types.NamespacedName{Name: common.AdmissionServiceAccountName, Namespace: falconAdmission.Spec.InstallNamespace}, existingServiceAccount) if err != nil && apierrors.IsNotFound(err) { diff --git a/controllers/falcon_container/image_push.go b/controllers/falcon_container/image_push.go index e226a23e..e4eaa7a7 100644 --- a/controllers/falcon_container/image_push.go +++ b/controllers/falcon_container/image_push.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -29,6 +30,11 @@ func (r *FalconContainerReconciler) PushImage(ctx context.Context, log logr.Logg return err } + // If we have version locking enabled (as it is by default), use the already configured version if present + if r.versionLock(falconContainer) { + return nil + } + pushAuth, err := r.pushAuth(ctx, falconContainer) if err != nil { return err @@ -38,11 +44,6 @@ func (r *FalconContainerReconciler) PushImage(ctx context.Context, log logr.Logg image := image.NewImageRefresher(ctx, log, r.falconApiConfig(ctx, falconContainer), pushAuth, falconContainer.Spec.Registry.TLS.InsecureSkipVerify) version := falconContainer.Spec.Version - // If we have version locking enabled (as it is by default), use the already configured version if present - if r.versionLock(falconContainer) { - return nil - } - tag, err := image.Refresh(registryUri, common.SensorTypeSidecar, version) if err != nil { return fmt.Errorf("Cannot push Falcon Container Image: %v", err) @@ -233,5 +234,5 @@ func (r *FalconContainerReconciler) imageMirroringEnabled(falconContainer *falco } func (r *FalconContainerReconciler) versionLock(falconContainer *falconv1alpha1.FalconContainer) bool { - return falconContainer.Spec.Version != nil && falconContainer.Status.Sensor != nil && *falconContainer.Spec.Version == *falconContainer.Status.Sensor + return (falconContainer.Spec.Version != nil && falconContainer.Status.Sensor != nil && strings.Contains(*falconContainer.Status.Sensor, *falconContainer.Spec.Version)) || (falconContainer.Spec.Version == nil && falconContainer.Status.Sensor != nil) } diff --git a/controllers/falcon_container/injector.go b/controllers/falcon_container/injector.go index e8c1b314..3751bac7 100644 --- a/controllers/falcon_container/injector.go +++ b/controllers/falcon_container/injector.go @@ -146,6 +146,11 @@ func (r *FalconContainerReconciler) reconcileDeployment(ctx context.Context, log update = true } + if !reflect.DeepEqual(deployment.Spec.Template.Spec.ImagePullSecrets, existingDeployment.Spec.Template.Spec.ImagePullSecrets) { + existingDeployment.Spec.Template.Spec.ImagePullSecrets = deployment.Spec.Template.Spec.ImagePullSecrets + update = true + } + if update { return existingDeployment, r.Update(ctx, log, falconContainer, existingDeployment) } diff --git a/docs/deployment/openshift/resources/admission/README.md b/docs/deployment/openshift/resources/admission/README.md index e481b8ae..eed6f419 100644 --- a/docs/deployment/openshift/resources/admission/README.md +++ b/docs/deployment/openshift/resources/admission/README.md @@ -52,7 +52,7 @@ spec: | registry.acr_name | (optional) Name of ACR for the Falcon Admission push. Only applicable to Azure cloud. (`registry.type="acr"`) | | resourcequota.pods | (optional) Configure the maximum number of pods that can be created in the falcon-kac namespace | | admissionConfig.serviceAccount.annotations| (optional) Configure annotations for the falcon-kac service account (e.g. for IAM role association) | -| admissionConfig.port | (optional) Configure the port the Falcon Admission Controller Service listens on | +| admissionConfig.servicePort | (optional) Configure the port the Falcon Admission Controller Service listens on | | admissionConfig.containerPort | (optional) Configure the port the Falcon Admission Controller container listens on | | admissionConfig.tls.validity | (optional) Configure the validity of the TLS certificate used by the Falcon Admission Controller | | admissionConfig.failurePolicy | (optional) Configure the failure policy of the Falcon Admission Controller | diff --git a/docs/resources/admission/README.md b/docs/resources/admission/README.md index e7dce943..22eaf603 100644 --- a/docs/resources/admission/README.md +++ b/docs/resources/admission/README.md @@ -52,7 +52,7 @@ spec: | registry.acr_name | (optional) Name of ACR for the Falcon Admission push. Only applicable to Azure cloud. (`registry.type="acr"`) | | resourcequota.pods | (optional) Configure the maximum number of pods that can be created in the falcon-kac namespace | | admissionConfig.serviceAccount.annotations| (optional) Configure annotations for the falcon-kac service account (e.g. for IAM role association) | -| admissionConfig.port | (optional) Configure the port the Falcon Admission Controller Service listens on | +| admissionConfig.servicePort | (optional) Configure the port the Falcon Admission Controller Service listens on | | admissionConfig.containerPort | (optional) Configure the port the Falcon Admission Controller container listens on | | admissionConfig.tls.validity | (optional) Configure the validity of the TLS certificate used by the Falcon Admission Controller | | admissionConfig.failurePolicy | (optional) Configure the failure policy of the Falcon Admission Controller | diff --git a/docs/src/resources/admission.md.tmpl b/docs/src/resources/admission.md.tmpl index a1db227e..f5d048e5 100644 --- a/docs/src/resources/admission.md.tmpl +++ b/docs/src/resources/admission.md.tmpl @@ -52,7 +52,7 @@ spec: | registry.acr_name | (optional) Name of ACR for the Falcon Admission push. Only applicable to Azure cloud. (`registry.type="acr"`) | | resourcequota.pods | (optional) Configure the maximum number of pods that can be created in the falcon-kac namespace | | admissionConfig.serviceAccount.annotations| (optional) Configure annotations for the falcon-kac service account (e.g. for IAM role association) | -| admissionConfig.port | (optional) Configure the port the Falcon Admission Controller Service listens on | +| admissionConfig.servicePort | (optional) Configure the port the Falcon Admission Controller Service listens on | | admissionConfig.containerPort | (optional) Configure the port the Falcon Admission Controller container listens on | | admissionConfig.tls.validity | (optional) Configure the validity of the TLS certificate used by the Falcon Admission Controller | | admissionConfig.failurePolicy | (optional) Configure the failure policy of the Falcon Admission Controller | diff --git a/internal/controller/assets/deployment.go b/internal/controller/assets/deployment.go index fdbc1d4f..17531afe 100644 --- a/internal/controller/assets/deployment.go +++ b/internal/controller/assets/deployment.go @@ -12,7 +12,6 @@ import ( // SideCarDeployment returns a Deployment object for the CrowdStrike Falcon sidecar func SideCarDeployment(name string, namespace string, component string, imageUri string, falconContainer *falconv1alpha1.FalconContainer) *appsv1.Deployment { - imagePullSecrets := []corev1.LocalObjectReference{{Name: common.FalconPullSecretName}} initContainerName := "crowdstrike-falcon-init-container" injectorConfigMapName := "falcon-sidecar-injector-config" registryCABundleConfigMapName := "falcon-sidecar-registry-certs" @@ -32,10 +31,6 @@ func SideCarDeployment(name string, namespace string, component string, imageUri var registryCAConfigMapName string = "" labels := common.CRLabels("deployment", name, component) - if common.FalconPullSecretName != falconContainer.Spec.Injector.ImagePullSecretName { - imagePullSecrets = append(imagePullSecrets, corev1.LocalObjectReference{Name: falconContainer.Spec.Injector.ImagePullSecretName}) - } - if falconContainer.Spec.Injector.Resources != nil { resources = falconContainer.Spec.Injector.Resources } @@ -130,7 +125,6 @@ func SideCarDeployment(name string, namespace string, component string, imageUri ReadOnly: true, MountPath: certPath, }) - } return &appsv1.Deployment{ @@ -187,7 +181,6 @@ func SideCarDeployment(name string, namespace string, component string, imageUri }, }, }, - ImagePullSecrets: imagePullSecrets, SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &runNonRoot, }, @@ -265,6 +258,8 @@ func AdmissionDeployment(name string, namespace string, component string, imageU sizeLimitTmp := resource.MustParse("256Mi") sizeLimitPrivate := resource.MustParse("4Ki") labels := common.CRLabels("deployment", name, component) + registryCAConfigMapName := "" + registryCABundleConfigMapName := name + "-registry-certs" if falconAdmission.Spec.AdmissionConfig.ResourcesClient != nil { resourcesClient = falconAdmission.Spec.AdmissionConfig.ResourcesClient @@ -274,6 +269,54 @@ func AdmissionDeployment(name string, namespace string, component string, imageU resourcesAC = falconAdmission.Spec.AdmissionConfig.ResourcesAC } + volumes := []corev1.Volume{ + { + Name: name + "-tls-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: name + "-tls", + }, + }, + }, + { + Name: "crowdstrike-falcon-vol0", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &sizeLimitTmp, + }, + }, + }, + { + Name: "crowdstrike-falcon-vol1", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &sizeLimitPrivate, + }, + }, + }, + } + + if falconAdmission.Spec.Registry.TLS.CACertificateConfigMap != "" { + registryCAConfigMapName = falconAdmission.Spec.Registry.TLS.CACertificateConfigMap + } + + if falconAdmission.Spec.Registry.TLS.CACertificate != "" { + registryCAConfigMapName = registryCABundleConfigMapName + } + + if registryCAConfigMapName != "" { + volumes = append(volumes, corev1.Volume{ + Name: registryCAConfigMapName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: registryCAConfigMapName, + }, + }, + }, + }) + } + return &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{ APIVersion: appsv1.SchemeGroupVersion.String(), @@ -329,7 +372,6 @@ func AdmissionDeployment(name string, namespace string, component string, imageU }, }, }, - ImagePullSecrets: pullSecretsAdmission(falconAdmission), ShareProcessNamespace: &shareProcessNamespace, SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &runNonRoot, @@ -401,21 +443,7 @@ func AdmissionDeployment(name string, namespace string, component string, imageU Protocol: corev1.ProtocolTCP, }, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: name + "-tls-certs", - MountPath: "/run/secrets/tls", - ReadOnly: true, - }, - { - Name: "crowdstrike-falcon-vol0", - MountPath: "/tmp", - }, - { - Name: "crowdstrike-falcon-vol1", - MountPath: "/var/private", - }, - }, + VolumeMounts: admissionDepVolumeMounts(name, registryCAConfigMapName, true), StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ @@ -470,16 +498,7 @@ func AdmissionDeployment(name string, namespace string, component string, imageU }, }, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "crowdstrike-falcon-vol0", - MountPath: "/tmp", - }, - { - Name: "crowdstrike-falcon-vol1", - MountPath: "/var/private", - }, - }, + VolumeMounts: admissionDepVolumeMounts(name, registryCAConfigMapName, false), StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ @@ -511,48 +530,44 @@ func AdmissionDeployment(name string, namespace string, component string, imageU Resources: *resourcesAC, }, }, - Volumes: []corev1.Volume{ - { - Name: name + "-tls-certs", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: name + "-tls", - }, - }, - }, - { - Name: "crowdstrike-falcon-vol0", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &sizeLimitTmp, - }, - }, - }, - { - Name: "crowdstrike-falcon-vol1", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &sizeLimitPrivate, - }, - }, - }, - }, + Volumes: volumes, }, }, }, } } -func pullSecretsAdmission(admission *falconv1alpha1.FalconAdmission) []corev1.LocalObjectReference { - if admission.Spec.Image == "" { - return []corev1.LocalObjectReference{ - { - Name: common.FalconPullSecretName, - }, - } - } else { - return admission.Spec.AdmissionConfig.ImagePullSecrets +func admissionDepVolumeMounts(name string, registryCAConfigMapName string, client bool) []corev1.VolumeMount { + certPath := "/etc/docker/certs.d/falcon-admission-certs" + + volumeMounts := []corev1.VolumeMount{ + { + Name: "crowdstrike-falcon-vol0", + MountPath: "/tmp", + }, + { + Name: "crowdstrike-falcon-vol1", + MountPath: "/var/private", + }, + } + + if client { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: name + "-tls-certs", + MountPath: "/run/secrets/tls", + ReadOnly: true, + }) } + + if registryCAConfigMapName != "" { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: registryCAConfigMapName, + ReadOnly: true, + MountPath: certPath, + }) + } + + return volumeMounts } func admissionDepUpdateStrategy(admission *falconv1alpha1.FalconAdmission) appsv1.DeploymentStrategy { diff --git a/internal/controller/assets/deployment_test.go b/internal/controller/assets/deployment_test.go index cd9db538..420853b6 100644 --- a/internal/controller/assets/deployment_test.go +++ b/internal/controller/assets/deployment_test.go @@ -48,36 +48,6 @@ func TestAdmissionDeployment(t *testing.T) { } } -// TestPullSecretsAdmission tests the PullSecretsAdmission function -func TestPullSecretsAdmission(t *testing.T) { - falconAdmission := falconv1alpha1.FalconAdmission{} - - want := []corev1.LocalObjectReference{ - { - Name: common.FalconPullSecretName, - }, - } - - got := pullSecretsAdmission(&falconAdmission) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("pullSecretsAdmission() mismatch (-want +got): %s", diff) - } - - want = []corev1.LocalObjectReference{ - { - Name: "testSecretName", - }, - } - - falconAdmission.Spec.Image = "testImageName" - falconAdmission.Spec.AdmissionConfig.ImagePullSecrets = want - - got = pullSecretsAdmission(&falconAdmission) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("pullSecretsAdmission() mismatch (-want +got): %s", diff) - } -} - // TestAdmissionDepUpdateStrategy tests the Admission Controller Deployment Update Strategy function func TestAdmissionDepUpdateStrategy(t *testing.T) { falconAdmission := falconv1alpha1.FalconAdmission{} @@ -109,7 +79,6 @@ func TestAdmissionDepUpdateStrategy(t *testing.T) { // testSideCarDeployment is a helper function to create a Deployment object for testing func testSideCarDeployment(name string, namespace string, component string, imageUri string, falconContainer *falconv1alpha1.FalconContainer) *appsv1.Deployment { replicas := int32(123) - imagePullSecrets := []corev1.LocalObjectReference{{Name: common.FalconPullSecretName}} initContainerName := "crowdstrike-falcon-init-container" injectorConfigMapName := "falcon-sidecar-injector-config" registryCABundleConfigMapName := "falcon-sidecar-registry-certs" @@ -129,10 +98,6 @@ func testSideCarDeployment(name string, namespace string, component string, imag var registryCAConfigMapName string = "" labels := common.CRLabels("deployment", name, component) - if common.FalconPullSecretName != falconContainer.Spec.Injector.ImagePullSecretName { - imagePullSecrets = append(imagePullSecrets, corev1.LocalObjectReference{Name: falconContainer.Spec.Injector.ImagePullSecretName}) - } - if falconContainer.Spec.Injector.Resources != nil { resources = falconContainer.Spec.Injector.Resources } @@ -283,7 +248,6 @@ func testSideCarDeployment(name string, namespace string, component string, imag }, }, }, - ImagePullSecrets: imagePullSecrets, SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &runNonRoot, }, @@ -424,7 +388,6 @@ func testAdmissionDeployment(name string, namespace string, component string, im }, }, }, - ImagePullSecrets: pullSecretsAdmission(falconAdmission), ShareProcessNamespace: &shareProcessNamespace, SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &runNonRoot, @@ -497,11 +460,6 @@ func testAdmissionDeployment(name string, namespace string, component string, im }, }, VolumeMounts: []corev1.VolumeMount{ - { - Name: name + "-tls-certs", - MountPath: "/run/secrets/tls", - ReadOnly: true, - }, { Name: "crowdstrike-falcon-vol0", MountPath: "/tmp", @@ -510,6 +468,11 @@ func testAdmissionDeployment(name string, namespace string, component string, im Name: "crowdstrike-falcon-vol1", MountPath: "/var/private", }, + { + Name: name + "-tls-certs", + MountPath: "/run/secrets/tls", + ReadOnly: true, + }, }, StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{