From 5668f0a38ace13be081ed7b94444421b241b9af9 Mon Sep 17 00:00:00 2001 From: Igor Eulalio Date: Mon, 16 Dec 2024 12:23:10 -0300 Subject: [PATCH] feat: add process based exceptions to drift policies Signed-off-by: Igor Eulalio --- sysdig/internal/client/v2/model.go | 12 +- sysdig/resource_sysdig_secure_drift_policy.go | 18 ++- ...esource_sysdig_secure_drift_policy_test.go | 41 +++++ sysdig/tfresource.go | 149 ++++++++++++++---- website/docs/r/secure_drift_policy.md | 4 + 5 files changed, 182 insertions(+), 42 deletions(-) diff --git a/sysdig/internal/client/v2/model.go b/sysdig/internal/client/v2/model.go index 500411a4..106eed0d 100644 --- a/sysdig/internal/client/v2/model.go +++ b/sysdig/internal/client/v2/model.go @@ -426,11 +426,13 @@ type RuntimePolicyRuleList struct { } type DriftRuleDetails struct { - RuleType ElementType `json:"ruleType"` - Exceptions *RuntimePolicyRuleList `json:"exceptionList"` - ProhibitedBinaries *RuntimePolicyRuleList `json:"prohibitedBinaries"` - Mode string `json:"mode"` - Details `json:"-"` + RuleType ElementType `json:"ruleType"` + Exceptions *RuntimePolicyRuleList `json:"exceptionList"` + ProcessBasedExceptions *RuntimePolicyRuleList `json:"allowlistProcess"` + ProcessBasedDenylist *RuntimePolicyRuleList `json:"denylistProcess"` + ProhibitedBinaries *RuntimePolicyRuleList `json:"prohibitedBinaries"` + Mode string `json:"mode"` + Details `json:"-"` } func (p DriftRuleDetails) GetRuleType() ElementType { diff --git a/sysdig/resource_sysdig_secure_drift_policy.go b/sysdig/resource_sysdig_secure_drift_policy.go index c1d4c679..6d869dc7 100644 --- a/sysdig/resource_sysdig_secure_drift_policy.go +++ b/sysdig/resource_sysdig_secure_drift_policy.go @@ -56,14 +56,16 @@ func resourceSysdigSecureDriftPolicy() *schema.Resource { Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "id": ReadOnlyIntSchema(), - "name": ReadOnlyStringSchema(), - "description": DescriptionSchema(), - "tags": TagsSchema(), - "version": VersionSchema(), - "enabled": BoolSchema(), // Enable maps to mode rule attribute - "exceptions": ExceptionsSchema(), - "prohibited_binaries": ExceptionsSchema(), + "id": ReadOnlyIntSchema(), + "name": ReadOnlyStringSchema(), + "description": DescriptionSchema(), + "tags": TagsSchema(), + "version": VersionSchema(), + "enabled": BoolSchema(), // Enable maps to mode rule attribute + "exceptions": ExceptionsSchema(), + "prohibited_binaries": ExceptionsSchema(), + "process_based_exceptions": ExceptionsSchema(), + "process_based_prohibited_binaries": ExceptionsSchema(), }, }, }, diff --git a/sysdig/resource_sysdig_secure_drift_policy_test.go b/sysdig/resource_sysdig_secure_drift_policy_test.go index 894bd797..0ff56faf 100644 --- a/sysdig/resource_sysdig_secure_drift_policy_test.go +++ b/sysdig/resource_sysdig_secure_drift_policy_test.go @@ -36,6 +36,9 @@ func TestAccDriftPolicy(t *testing.T) { { Config: driftPolicyWithoutNotificationChannel(rText()), }, + { + Config: driftPolicyWithoutExceptions(rText()), + }, }, }) } @@ -61,6 +64,9 @@ resource "sysdig_secure_drift_policy" "sample" { prohibited_binaries { items = ["/usr/bin/curl"] } + process_based_exceptions { + items = ["/usr/bin/curl"] + } } actions { @@ -94,6 +100,9 @@ resource "sysdig_secure_drift_policy" "sample" { prohibited_binaries { items = ["/usr/bin/curl"] } + process_based_exceptions { + items = ["/usr/bin/curl"] + } } actions { @@ -133,6 +142,9 @@ resource "sysdig_secure_drift_policy" "sample" { prohibited_binaries { items = ["/usr/bin/curl"] } + process_based_exceptions { + items = ["/usr/bin/curl"] + } } actions {} @@ -162,6 +174,9 @@ resource "sysdig_secure_drift_policy" "sample" { prohibited_binaries { items = ["/usr/bin/curl"] } + process_based_exceptions { + items = ["/usr/bin/curl"] + } } actions { @@ -171,3 +186,29 @@ resource "sysdig_secure_drift_policy" "sample" { `, name) } + +func driftPolicyWithoutExceptions(name string) string { + return fmt.Sprintf(` +%s + +resource "sysdig_secure_drift_policy" "sample" { + name = "Test Drift Policy %s" + description = "Test Drift Policy Description" + enabled = true + severity = 4 + + rule { + description = "Test Drift Rule Description" + + enabled = true + } + + actions { + prevent_drift = true + } + + notification_channels = [sysdig_secure_notification_channel_email.sample_email.id] +} + +`, secureNotificationChannelEmailWithName(name), name) +} diff --git a/sysdig/tfresource.go b/sysdig/tfresource.go index 2142fb9f..6464667d 100644 --- a/sysdig/tfresource.go +++ b/sysdig/tfresource.go @@ -143,38 +143,97 @@ func setTFResourcePolicyRulesDrift(d *schema.ResourceData, policy v2.PolicyRules return errors.New("The policy must have at least one rule attached to it") } - rules := []map[string]interface{}{} + var rules []map[string]interface{} for _, rule := range policy.Rules { - // Only a single block of exceptions and prohibited binaries is allowed - exceptions := []map[string]interface{}{{ - "items": rule.Details.(*v2.DriftRuleDetails).Exceptions.Items, - "match_items": rule.Details.(*v2.DriftRuleDetails).Exceptions.MatchItems, - }} + driftDetails, ok := rule.Details.(*v2.DriftRuleDetails) + if !ok { + return errors.New("unexpected rule details type, expected DriftRuleDetails") + } + + // Directly use fields assuming backend returns zero values (not nil) + exceptionsItems := driftDetails.Exceptions.Items + exceptionsMatchItems := driftDetails.Exceptions.MatchItems + + var exceptionsBlock []map[string]interface{} + if len(exceptionsItems) > 0 || exceptionsMatchItems { + exceptionsBlock = []map[string]interface{}{ + { + "items": exceptionsItems, + "match_items": exceptionsMatchItems, + }, + } + } - prohibitedBinaries := []map[string]interface{}{{ - "items": rule.Details.(*v2.DriftRuleDetails).ProhibitedBinaries.Items, - "match_items": rule.Details.(*v2.DriftRuleDetails).ProhibitedBinaries.MatchItems, - }} + prohibitedItems := driftDetails.ProhibitedBinaries.Items + prohibitedMatchItems := driftDetails.ProhibitedBinaries.MatchItems - mode := rule.Details.(*v2.DriftRuleDetails).Mode - enabled := true - if mode == "disabled" { - enabled = false + var prohibitedBinariesBlock []map[string]interface{} + if len(prohibitedItems) > 0 || prohibitedMatchItems { + prohibitedBinariesBlock = []map[string]interface{}{ + { + "items": prohibitedItems, + "match_items": prohibitedMatchItems, + }, + } } - rules = append(rules, map[string]interface{}{ - "id": rule.Id, - "name": rule.Name, - "description": rule.Description, - "version": rule.Version, - "tags": rule.Tags, - "enabled": enabled, - "exceptions": exceptions, - "prohibited_binaries": prohibitedBinaries, - }) + processBasedExceptionsItems := driftDetails.ProcessBasedExceptions.Items + processBasedExceptionMatchItems := driftDetails.ProcessBasedExceptions.MatchItems + + var processBasedExceptionsBlock []map[string]interface{} + if len(processBasedExceptionsItems) > 0 || processBasedExceptionMatchItems { + processBasedExceptionsBlock = []map[string]interface{}{ + { + "items": processBasedExceptionsItems, + "match_items": processBasedExceptionMatchItems, + }, + } + } + + processBasedProhibitedBinariesItems := driftDetails.ProcessBasedDenylist.Items + processBasedProhibitedBinariesMatchItems := driftDetails.ProcessBasedDenylist.MatchItems + + var processBasedProhibitedBinariesBlock []map[string]interface{} + if len(processBasedProhibitedBinariesItems) > 0 || processBasedProhibitedBinariesMatchItems { + processBasedProhibitedBinariesBlock = []map[string]interface{}{ + { + "items": processBasedProhibitedBinariesItems, + "match_items": processBasedProhibitedBinariesMatchItems, + }, + } + } + + mode := driftDetails.Mode + enabled := (mode != "disabled") + + ruleMap := map[string]interface{}{ + "id": rule.Id, + "name": rule.Name, + "description": rule.Description, + "version": rule.Version, + "tags": rule.Tags, + "enabled": enabled, + } + + if exceptionsBlock != nil { + ruleMap["exceptions"] = exceptionsBlock + } + if prohibitedBinariesBlock != nil { + ruleMap["prohibited_binaries"] = prohibitedBinariesBlock + } + if processBasedExceptionsBlock != nil { + ruleMap["process_based_exceptions"] = processBasedExceptionsBlock + } + if processBasedProhibitedBinariesBlock != nil { + ruleMap["process_based_prohibited_binaries"] = processBasedProhibitedBinariesBlock + } + + rules = append(rules, ruleMap) } - _ = d.Set("rule", rules) + if err := d.Set("rule", rules); err != nil { + return err + } return nil } @@ -418,6 +477,10 @@ func setPolicyRulesDrift(policy *v2.PolicyRulesComposite, d *schema.ResourceData if _, ok := d.GetOk("rule.0.exceptions"); ok { // TODO: Do not hardcode the indexes exceptions.Items = schemaSetToList(d.Get("rule.0.exceptions.0.items")) exceptions.MatchItems = d.Get("rule.0.exceptions.0.match_items").(bool) + } else { + // initialize Items and MatchItems so we comply with structure and not generate drift + exceptions.Items = []string{} + exceptions.MatchItems = false } // TODO: Extract into a function @@ -425,6 +488,32 @@ func setPolicyRulesDrift(policy *v2.PolicyRulesComposite, d *schema.ResourceData if _, ok := d.GetOk("rule.0.prohibited_binaries"); ok { // TODO: Do not hardcode the indexes prohibitedBinaries.Items = schemaSetToList(d.Get("rule.0.prohibited_binaries.0.items")) prohibitedBinaries.MatchItems = d.Get("rule.0.prohibited_binaries.0.match_items").(bool) + } else { + // initialize Items and MatchItems so we comply with structure and not generate drift + prohibitedBinaries.Items = []string{} + prohibitedBinaries.MatchItems = false + } + + // TODO: Extract into a function + processBasedExceptions := &v2.RuntimePolicyRuleList{} + if _, ok := d.GetOk("rule.0.process_based_exceptions"); ok { // TODO: Do not hardcode the indexes + processBasedExceptions.Items = schemaSetToList(d.Get("rule.0.process_based_exceptions.0.items")) + processBasedExceptions.MatchItems = d.Get("rule.0.process_based_exceptions.0.match_items").(bool) + } else { + // initialize Items and MatchItems so we comply with structure and not generate drift + processBasedExceptions.Items = []string{} + processBasedExceptions.MatchItems = false + } + + // TODO: Extract into a function + processBasedProhibitedBinaries := &v2.RuntimePolicyRuleList{} + if _, ok := d.GetOk("rule.0.process_based_prohibited_binaries"); ok { // TODO: Do not hardcode the indexes + processBasedProhibitedBinaries.Items = schemaSetToList(d.Get("rule.0.process_based_prohibited_binaries.0.items")) + processBasedProhibitedBinaries.MatchItems = d.Get("rule.0.process_based_prohibited_binaries.0.match_items").(bool) + } else { + // initialize Items and MatchItems so we comply with structure and not generate drift + processBasedProhibitedBinaries.Items = []string{} + processBasedProhibitedBinaries.MatchItems = false } tags := schemaSetToList(d.Get("rule.0.tags")) @@ -445,10 +534,12 @@ func setPolicyRulesDrift(policy *v2.PolicyRulesComposite, d *schema.ResourceData Description: d.Get("rule.0.description").(string), Tags: tags, Details: v2.DriftRuleDetails{ - RuleType: v2.ElementType("DRIFT"), // TODO: Use const - Mode: mode, - Exceptions: exceptions, - ProhibitedBinaries: prohibitedBinaries, + RuleType: v2.ElementType("DRIFT"), // TODO: Use const + Mode: mode, + Exceptions: exceptions, + ProhibitedBinaries: prohibitedBinaries, + ProcessBasedExceptions: processBasedExceptions, + ProcessBasedDenylist: processBasedProhibitedBinaries, }, } diff --git a/website/docs/r/secure_drift_policy.md b/website/docs/r/secure_drift_policy.md index 6db51112..5f4afd7f 100644 --- a/website/docs/r/secure_drift_policy.md +++ b/website/docs/r/secure_drift_policy.md @@ -118,6 +118,10 @@ The rule block is required and supports: * `items` - (Required) Specify comma separated list of exceptions, e.g. `/usr/bin/rm, /usr/bin/curl`. * `prohibited_binaries` - (Optional) A prohibited binary can be a known harmful binary or one that facilitates discovery of your environment. * `items` - (Required) Specify comma separated list of prohibited binaries, e.g. `/usr/bin/rm, /usr/bin/curl`. +* `process_based_exceptions` - (Optional) List of processes that will be able to execute a drifted file + * `items` - (Required) Specify comma separated list of processes, e.g. `/usr/bin/rm, /usr/bin/curl`. +* `process_based_prohibited_binaries` - (Optional) List of processes that will be prohibited to execute a drifted file + * `items` - (Required) Specify comma separated list of processes, e.g. `/usr/bin/rm, /usr/bin/curl`.