Skip to content

Commit

Permalink
Merge pull request #505 from redhatrises/allow_sensor_ns_kustomization
Browse files Browse the repository at this point in the history
feat: allow node sensor to customize namespace
  • Loading branch information
redhatrises authored May 2, 2024
2 parents 31da5df + 2a5496c commit 946e0cf
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 39 deletions.
6 changes: 0 additions & 6 deletions api/falcon/v1alpha1/falconnodesensor_funcs.go

This file was deleted.

7 changes: 7 additions & 0 deletions api/falcon/v1alpha1/falconnodesensor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ type FalconNodeSensorSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Namespace where the Falcon Sensor should be installed.
// For best security practices, this should be a dedicated namespace that is not used for any other purpose.
// It also should not be the same namespace where the Falcon Operator, or other Falcon resources are deployed.
// +kubebuilder:default:=falcon-system
// +operator-sdk:csv:customresourcedefinitions:type=spec,order=1,xDescriptors={"urn:alm:descriptor:io.kubernetes:Namespace"}
InstallNamespace string `json:"installNamespace,omitempty"`

// Various configuration for DaemonSet Deployment
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="DaemonSet Configuration",order=3
Node FalconNodeSensorConfig `json:"node,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ spec:
- client_secret
- cloud_region
type: object
installNamespace:
default: falcon-system
description: Namespace where the Falcon Sensor should be installed.
For best security practices, this should be a dedicated namespace
that is not used for any other purpose. It also should not be the
same namespace where the Falcon Operator, or other Falcon resources
are deployed.
type: string
node:
description: Various configuration for DaemonSet Deployment
properties:
Expand Down
8 changes: 8 additions & 0 deletions deploy/falcon-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,14 @@ spec:
- client_secret
- cloud_region
type: object
installNamespace:
default: falcon-system
description: Namespace where the Falcon Sensor should be installed.
For best security practices, this should be a dedicated namespace
that is not used for any other purpose. It also should not be the
same namespace where the Falcon Operator, or other Falcon resources
are deployed.
type: string
node:
description: Various configuration for DaemonSet Deployment
properties:
Expand Down
1 change: 1 addition & 0 deletions docs/deployment/openshift/resources/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ spec:
#### Node Configuration Settings
| Spec | Description |
| :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| installNamespace | (optional) Override the default namespace of falcon-system |
| node.tolerations | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for examples on configuring tolerations |
| node.nodeAffinity | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ for examples on configuring nodeAffinity |
| node.image | (optional) Location of the Falcon Sensor Image. Specify only when you mirror the original image to your own image repository |
Expand Down
1 change: 1 addition & 0 deletions docs/resources/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ spec:
#### Node Configuration Settings
| Spec | Description |
| :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| installNamespace | (optional) Override the default namespace of falcon-system |
| node.tolerations | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for examples on configuring tolerations |
| node.nodeAffinity | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ for examples on configuring nodeAffinity |
| node.image | (optional) Location of the Falcon Sensor Image. Specify only when you mirror the original image to your own image repository |
Expand Down
1 change: 1 addition & 0 deletions docs/src/resources/node.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ spec:
#### Node Configuration Settings
| Spec | Description |
| :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| installNamespace | (optional) Override the default namespace of falcon-system |
| node.tolerations | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ for examples on configuring tolerations |
| node.nodeAffinity | (optional) See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ for examples on configuring nodeAffinity |
| node.image | (optional) Location of the Falcon Sensor Image. Specify only when you mirror the original image to your own image repository |
Expand Down
19 changes: 19 additions & 0 deletions internal/controller/admission/falconadmission_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,25 @@ func (r *FalconAdmissionReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, err
}

validate, err := k8sutils.CheckRunningPodLabels(r.Client, ctx, falconAdmission.Spec.InstallNamespace, common.CRLabels("deployment", falconAdmission.Name, common.FalconAdmissionController))
if err != nil {
return ctrl.Result{}, err
}
if !validate {
err = k8sutils.ConditionsUpdate(r.Client, ctx, req, log, falconAdmission, &falconAdmission.Status, metav1.Condition{
Status: metav1.ConditionFalse,
Reason: falconv1alpha1.ReasonReqNotMet,
Type: falconv1alpha1.ConditionFailed,
Message: "FalconAdmission must not be installed in a namespace with other workloads running. Please change the namespace in the CR configuration.",
ObservedGeneration: falconAdmission.GetGeneration(),
})
if err != nil {
return ctrl.Result{}, err
}
log.Error(nil, "FalconAdmission is attempting to install in a namespace with existing pods. Please update the CR configuration to a namespace that does not have workoads already running.")
return ctrl.Result{}, err
}

// Let's just set the status as Unknown when no status is available
if falconAdmission.Status.Conditions == nil || len(falconAdmission.Status.Conditions) == 0 {
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/assets/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func Daemonset(dsName, image, serviceAccount string, node *falconv1alpha1.Falcon
return &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: dsName,
Namespace: node.TargetNs(),
Namespace: node.Spec.InstallNamespace,
Labels: common.CRLabels("daemonset", dsName, common.FalconKernelSensor),
},
Spec: appsv1.DaemonSetSpec{
Expand Down Expand Up @@ -286,7 +286,7 @@ func RemoveNodeDirDaemonset(dsName, image, serviceAccount string, node *falconv1
return &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: dsName,
Namespace: node.TargetNs(),
Namespace: node.Spec.InstallNamespace,
Labels: common.CRLabels("cleanup", dsName, common.FalconKernelSensor),
},
Spec: appsv1.DaemonSetSpec{
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/assets/daemonset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func TestDaemonset(t *testing.T) {
want := &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: dsName,
Namespace: falconNode.Namespace,
Namespace: falconNode.Spec.InstallNamespace,
Labels: common.CRLabels("daemonset", dsName, common.FalconKernelSensor),
},
Spec: appsv1.DaemonSetSpec{
Expand Down Expand Up @@ -310,7 +310,7 @@ func TestRemoveNodeDirDaemonset(t *testing.T) {
want := &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: dsName,
Namespace: falconNode.Namespace,
Namespace: falconNode.Spec.InstallNamespace,
Labels: common.CRLabels("cleanup", dsName, common.FalconKernelSensor),
},
Spec: appsv1.DaemonSetSpec{
Expand Down
24 changes: 24 additions & 0 deletions internal/controller/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ func ConditionsUpdate(r client.Client, ctx context.Context, req ctrl.Request, lo
return nil
}

func CheckRunningPodLabels(r client.Client, ctx context.Context, namespace string, matchingLabels client.MatchingLabels) (bool, error) {
podList := &corev1.PodList{}

listOpts := []client.ListOption{
client.InNamespace(namespace),
}

if err := r.List(ctx, podList, listOpts...); err != nil {
return false, fmt.Errorf("unable to list pods: %v", err)
}

for _, pod := range podList.Items {
if pod.ObjectMeta.Labels != nil {
for k, v := range matchingLabels {
if pod.ObjectMeta.Labels[k] != v {
return false, nil
}
}
}
}

return true, nil
}

func GetReadyPod(r client.Client, ctx context.Context, namespace string, matchingLabels client.MatchingLabels) (*corev1.Pod, error) {
podList := &corev1.PodList{}
listOpts := []client.ListOption{
Expand Down
45 changes: 45 additions & 0 deletions internal/controller/common/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,51 @@ func getFakeClient(initObjs ...client.Object) (client.WithWatch, error) {
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build(), nil
}

func TestCheckRunningPodLabels(t *testing.T) {
ctx := context.Background()

fakeClient, err := getFakeClient()
if err != nil {
t.Fatalf("TestCheckRunningPodLabels getFakeClient() error = %v", err)
}

err = fakeClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}})
if err != nil {
t.Fatalf("TestCheckRunningPodLabels Create() error = %v", err)
}

testLabel := map[string]string{"crowdstrike.com/provider": "crowdstrike", "testLabel": "testPod"}
err = fakeClient.Create(ctx, &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "test-namespace", Labels: testLabel}})
if err != nil {
t.Fatalf("TestCheckRunningPodLabels Create() error = %v", err)
}

matchingLabels := client.MatchingLabels{"testLabel": "testPod"}

got, err := CheckRunningPodLabels(fakeClient, ctx, "test-namespace", matchingLabels)
if err != nil {
t.Errorf("CheckRunningPodLabels() error = %v", err)
}

want := true
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("CheckRunningPodLabels() mismatch (-want +got):\n%s", diff)
}

// Test with non-matching labels
matchingLabels = client.MatchingLabels{"testLabel": "nonMatchingValue"}

got, err = CheckRunningPodLabels(fakeClient, ctx, "test-namespace", matchingLabels)
if err != nil {
t.Errorf("CheckRunningPodLabels() error = %v", err)
}

want = false
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("CheckRunningPodLabels() mismatch (-want +got):\n%s", diff)
}
}

func TestGetReadyPod(t *testing.T) {
ctx := context.Background()

Expand Down
Loading

0 comments on commit 946e0cf

Please sign in to comment.