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..b872f0c1 100644 --- a/sysdig/tfresource.go +++ b/sysdig/tfresource.go @@ -22,6 +22,8 @@ const ( defaultMalwareTag = "malware" defaultDriftTag = "drift" defaultMLTag = "machine_learning" + + driftElementType = "DRIFT" ) type Target interface { @@ -143,38 +145,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 } @@ -414,18 +475,13 @@ func setPolicyRulesDrift(policy *v2.PolicyRulesComposite, d *schema.ResourceData // TODO: Iterate over a list of rules instead of hard-coding the index values // TODO: Should we assume that only a single Malware rule can be attached to a policy? - exceptions := &v2.RuntimePolicyRuleList{} - 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) - } + exceptions := extractIntoRuntimePolicyRuleList("rule.0.exceptions", d) - // TODO: Extract into a function - prohibitedBinaries := &v2.RuntimePolicyRuleList{} - 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) - } + prohibitedBinaries := extractIntoRuntimePolicyRuleList("rule.0.prohibited_binaries", d) + + processBasedExceptions := extractIntoRuntimePolicyRuleList("rule.0.process_based_exceptions", d) + + processBasedProhibitedBinaries := extractIntoRuntimePolicyRuleList("rule.0.process_based_prohibited_binaries", d) tags := schemaSetToList(d.Get("rule.0.tags")) // Set default tags as field tags must not be null @@ -445,10 +501,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(driftElementType), // TODO: Use const + Mode: mode, + Exceptions: &exceptions, + ProhibitedBinaries: &prohibitedBinaries, + ProcessBasedExceptions: &processBasedExceptions, + ProcessBasedDenylist: &processBasedProhibitedBinaries, }, } @@ -468,6 +526,22 @@ func setPolicyRulesDrift(policy *v2.PolicyRulesComposite, d *schema.ResourceData return nil } +func extractIntoRuntimePolicyRuleList(key string, d *schema.ResourceData) v2.RuntimePolicyRuleList { + if _, ok := d.GetOk(key); ok { + items := schemaSetToList(d.Get(key + ".0.items")) + matchItems := d.Get(key + ".0.match_items").(bool) + + return v2.RuntimePolicyRuleList{ + Items: items, + MatchItems: matchItems, + } + } + return v2.RuntimePolicyRuleList{ + Items: []string{}, + MatchItems: false, + } +} + func setPolicyRulesML(policy *v2.PolicyRulesComposite, d *schema.ResourceData) error { policy.Policy.Rules = []*v2.PolicyRule{} policy.Rules = []*v2.RuntimePolicyRule{} 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`.