Skip to content

Commit

Permalink
Adding a new field in ScanSetting to disable remediation
Browse files Browse the repository at this point in the history
In order to prevent remediation gets applied unexpected, we are adding a new field to the ScanSetting, so user can set disableRemediations to true so Compliance Operator will not apply any remediation.
  • Loading branch information
Vincent056 committed Mar 25, 2024
1 parent 5b4e351 commit 9c3d01b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ spec:
automatically. This is done by deleting the "outdated" object from
the remediation.
type: boolean
disableRemediations:
default: false
description: Defines whether remediations should be allowed to be
applied
type: boolean
scans:
description: Contains a list of the scans to execute on the cluster
items:
Expand Down
4 changes: 4 additions & 0 deletions config/crd/bases/compliance.openshift.io_scansettings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ spec:
debug:
description: Enable debug logging of workloads and OpenSCAP
type: boolean
disableRemediations:
default: false
description: Defines whether remediations should be allowed to be applied
type: boolean
httpsProxy:
description: It is recommended to set the proxy via the config.openshift.io/Proxy
object Defines a proxy for the scan to get external resources from.
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/compliance/v1alpha1/compliancesuite_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ type ComplianceSuiteSettings struct {
// defaulting to False.
// +kubebuilder:default=false
Suspend bool `json:"suspend,omitempty"`
// Defines whether remediations should be allowed to be applied
// +kubebuilder:default=false
DisableRemediations bool `json:"disableRemediations,omitempty"`
}

// ComplianceSuiteSpec defines the desired state of ComplianceSuite
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,25 @@ func (r *ReconcileComplianceRemediation) Reconcile(ctx context.Context, request
return reconcile.Result{}, nil
}

func (r *ReconcileComplianceRemediation) canApplyRemediation(remediationInstance *compv1alpha1.ComplianceRemediation) (bool, error) {
suiteName := remediationInstance.GetLabels()[compv1alpha1.SuiteLabel]
if suiteName == "" {
return false, fmt.Errorf("no suite label found")
}

suite := &compv1alpha1.ComplianceSuite{}
if err := r.Client.Get(context.TODO(), types.NamespacedName{
Name: suiteName, Namespace: remediationInstance.Namespace}, suite); err != nil {
return false, fmt.Errorf("couldn't get suite: %w", err)
}

if suite.Spec.DisableRemediations {
return false, nil
}

return true, nil
}

// Gets a remediation and ensures the object exists in the cluster if the
// remediation if applicable
func (r *ReconcileComplianceRemediation) reconcileRemediation(instance *compv1alpha1.ComplianceRemediation, logger logr.Logger) error {
Expand All @@ -212,10 +231,12 @@ func (r *ReconcileComplianceRemediation) reconcileRemediation(instance *compv1al

objectLogger := logger.WithValues("Object.Name", obj.GetName(), "Object.Namespace", obj.GetNamespace(), "Object.Kind", obj.GetKind())
objectLogger.Info("Reconciling remediation object")

canApplyRemediation, err := r.canApplyRemediation(instance)
if err != nil {
return fmt.Errorf("failed to check if remediation can be applied: %w", err)
}
found := obj.DeepCopy()
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found)

err = r.Client.Get(context.TODO(), types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, found)
if kerrors.IsForbidden(err) {
return common.NewNonRetriableCtrlError(
"Unable to get fix object from ComplianceRemediation. "+
Expand All @@ -234,6 +255,9 @@ func (r *ReconcileComplianceRemediation) reconcileRemediation(instance *compv1al
if err != nil {
return fmt.Errorf("failed to set related remediations to apply: %w", err)
}
if !canApplyRemediation {
return errors.New("We won't create the remediation because DisableRemediations is set to true in the ScanSetting")
}
err = r.createRemediation(obj, objectLogger)
if err != nil {
return fmt.Errorf("failed to create remediation: %w", err)
Expand All @@ -251,6 +275,9 @@ func (r *ReconcileComplianceRemediation) reconcileRemediation(instance *compv1al
if err != nil {
return fmt.Errorf("failed to set related remediations to apply: %w", err)
}
if !canApplyRemediation {
return errors.New("We won't patch the remediation because DisableRemediations is set to true in the ScanSetting")
}
return r.patchRemediation(obj, objectLogger)
}
err = r.setRemediations(instance, objectLogger, false)
Expand Down
67 changes: 67 additions & 0 deletions tests/e2e/serial/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,73 @@ func TestKubeletConfigRemediation(t *testing.T) {
if remediation.Status.ApplicationState != compv1alpha1.RemediationApplied {
t.Fatalf("remediation %s is not applied, but %s", remName, remediation.Status.ApplicationState)
}

// Now we want to update the value of the variable disableRemediations to true in ScanSetting
ssInstance := &compv1alpha1.ScanSetting{}
ssNsName := types.NamespacedName{
Name: "e2e-default-auto-apply",
Namespace: f.OperatorNamespace,
}
err = f.Client.Get(context.TODO(), ssNsName, ssInstance)
if err != nil {
t.Fatalf("couldn't get scan setting %s: %s", ssNsName.Name, err)
}

// Update the value
ssInstance.DisableRemediations = true
err = f.Client.Update(context.Background(), ssInstance)
if err != nil {
t.Fatalf("failed to update scan setting %s: %s", ssNsName.Name, err)
}

// Now we get the tp and update the value
tpInstance := &compv1alpha1.TailoredProfile{}
tpNsName := types.NamespacedName{
Name: tp.Name,
Namespace: f.OperatorNamespace,
}
err = f.Client.Get(context.TODO(), tpNsName, tpInstance)
if err != nil {
t.Fatalf("couldn't get tailored profile %s: %s", tp.Name, err)
}

// Update the value
tpInstance.Spec.SetValues[0].Value = "9h0m0s"
err = f.Client.Update(context.Background(), tpInstance)
if err != nil {
t.Fatalf("failed to update tailored profile %s: %s", tp.Name, err)
}

// Now we re-run the scan
err = f.ReRunScan(scanName, f.OperatorNamespace)
if err != nil {
t.Fatal(err)
}

// Scan has been re-started
log.Printf("scan phase should be reset")
err = f.WaitForSuiteScansStatus(f.OperatorNamespace, suiteName, compv1alpha1.PhaseRunning, compv1alpha1.ResultNotAvailable)
if err != nil {
t.Fatal(err)
}

// Ensure that all the scans in the suite have finished and are marked as Done
log.Printf("let's wait for it to be done now")
err = f.WaitForSuiteScansStatus(f.OperatorNamespace, suiteName, compv1alpha1.PhaseDone, compv1alpha1.ResultNonCompliant)
if err != nil {
t.Fatal(err)
}
log.Printf("scan re-run has finished")

// Now check the remediation is not in error state
err = f.Client.Get(context.TODO(), remNsName, remediation)
if err != nil {
t.Fatalf("couldn't get remediation %s: %s", remName, err)
}
if remediation.Status.ApplicationState != compv1alpha1.RemediationError {
t.Fatalf("remediation %s is not in error state, but %s", remName, remediation.Status.ApplicationState)
}

}

func TestSuspendScanSetting(t *testing.T) {
Expand Down

0 comments on commit 9c3d01b

Please sign in to comment.