Skip to content

Commit

Permalink
feat(secure): Add Malware, Drift, ML and AWS ML policy resources (#476)
Browse files Browse the repository at this point in the history
* refactor: Add common schema

* feat: Add composite policy resource

* feat: Add composite policy data source

* docs: Add docs for composite policy resource and data source

* chore: Fix formatting issues

* chore: Update TODO comments in composite policies client

* feat: Filter policies by type and name

* chore: Clean up code

* chore: Update the docs

* chore: Fix formatting issue

* chore: Fix Sprintf args in test code

* fix: Remove ability enable or disable individual rules

* chore: Fix lint issues

* refactor: Introduce reducer pattern

* refactor: Use a consistent way to get policy ID

* fix: Resolve copylock error

* refactor: Rename Composite to Malware policy

* chore: Fix Linter errors

* Fix: Resolve compilation error

* feat: Add Drift policy resource and data source

* test: Update Malware policy tests

* docs: Update Malware policy docs

* feat: Add ML policy

* fix: Ensure the version number is sent to Policies API in order to update resources

* chore: Update Drift policy docs

* fix: Resolve Lin issue

* feat: Add AWS ML policy

* chore: Fix docs typo

* fix: Add AWS ML policy to TF provider

* fix: Resolve "Setting state: Invalid address to set" error

* fix: Resolve compilation error

* fix: Skip version 0 to resolve resource update error

* fix: Resolve tfproviderdocs check error

* fix: Resolve "Invalid address to set" in drift policy resource

* fix: Fix resource_sysdig_secure_policy_test test failure

Error: expected [{pathStepImpl:{} Name:type}] to be one of [falco list_matching k8s_audit aws_cloudtrail gcp_auditlog azure_platformlogs okta github malware drift aws_machine_learning machine_learning], got awscloudtrail

* fix: Fix TestAccMalwarePolicy test failure

Error: Invalid argument name. Argument names must not be quoted.

* fix: Fix TestAccDriftPolicy test failure

* feat: Hide tags

* feat: Replace Drift rule Mode attribute with a boolean Enabled attribute

* fix: Fix Drift rule reducer

* fix: Hide Drift rule's match_items attribute

* fix: Fix "Invalid resource type" in TestAccMalwarePolicy test

* refactor: Remove details block and rename rules to rule

* fix: Fix "provide at least one rule name" test error

* docs: Update docs to include the latest TF resource changes

* test: Update tests

* chore: Update schema formatting

* fix: Escape query param

* refactor: Use const values

* tests: Generate unique policy name in data source tests

* tests: Add more tests to resources

* remove unnecessary changes

* more small fixes

* set hash alias field as optional

* remove hash alias references

---------

Co-authored-by: kmvachhani <[email protected]>
Co-authored-by: kmvachhani <[email protected]>
  • Loading branch information
3 people authored Feb 27, 2024
1 parent cfb1afe commit 83ef948
Show file tree
Hide file tree
Showing 31 changed files with 4,389 additions and 103 deletions.
104 changes: 104 additions & 0 deletions sysdig/data_source_sysdig_secure_aws_ml_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package sysdig

import (
"context"
"time"

v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceSysdigSecureAWSMLPolicy() *schema.Resource {
timeout := 5 * time.Minute

return &schema.Resource{
ReadContext: dataSourceSysdigSecureAWSMLPolicyRead,

Timeouts: &schema.ResourceTimeout{
Read: schema.DefaultTimeout(timeout),
},

Schema: createAWSMLPolicyDataSourceSchema(),
}
}

func dataSourceSysdigSecureAWSMLPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
return awsMLPolicyDataSourceRead(ctx, d, meta, "custom policy", isCustomCompositePolicy)
}

func createAWSMLPolicyDataSourceSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
// IMPORTANT: Type is implicit: It's automatically added upon conversion to JSON
"type": {
Type: schema.TypeString,
Computed: true,
},
"name": NameSchema(),
"description": DescriptionComputedSchema(),
"enabled": EnabledComputedSchema(),
"severity": SeverityComputedSchema(),
"scope": ScopeComputedSchema(),
"version": VersionSchema(),
"notification_channels": NotificationChannelsComputedSchema(),
"runbook": RunbookComputedSchema(),
"rule": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": ReadOnlyIntSchema(),
"name": ReadOnlyStringSchema(),
"description": DescriptionComputedSchema(),
"tags": TagsSchema(),
"version": VersionSchema(),
"anomalous_console_login": MLRuleThresholdAndSeverityComputedSchema(),
},
},
},
}
}

func awsMLPolicyDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}, resourceName string, validationFunc func(v2.PolicyRulesComposite) bool) diag.Diagnostics {
client, err := getSecureCompositePolicyClient(meta.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

policyName := d.Get("name").(string)
policyType := policyTypeAWSML

policies, _, err := client.FilterCompositePoliciesByNameAndType(ctx, policyType, policyName)
if err != nil {
return diag.FromErr(err)
}

var policy v2.PolicyRulesComposite
for _, existingPolicy := range policies {
tflog.Debug(ctx, "Filtered policies", map[string]interface{}{"name": existingPolicy.Policy.Name})

if existingPolicy.Policy.Name == policyName && existingPolicy.Policy.Type == policyType {
if !validationFunc(existingPolicy) {
return diag.Errorf("policy is not a %s", resourceName)
}
policy = existingPolicy
break
}
}

if policy.Policy == nil {
return diag.Errorf("unable to find policy %s", resourceName)
}

if policy.Policy.ID == 0 {
return diag.Errorf("unable to find %s", resourceName)
}

err = awsMLPolicyToResourceData(&policy, d)
if err != nil {
return diag.FromErr(err)
}

return nil
}
64 changes: 64 additions & 0 deletions sysdig/data_source_sysdig_secure_aws_ml_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//go:build tf_acc_sysdig_secure || tf_acc_policies || tf_acc_onprem_secure

package sysdig_test

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/draios/terraform-provider-sysdig/sysdig"
)

func TestAccAWSMLPolicyDataSource(t *testing.T) {
rText := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
if v := os.Getenv("SYSDIG_SECURE_API_TOKEN"); v == "" {
t.Fatal("SYSDIG_SECURE_API_TOKEN must be set for acceptance tests")
}
},
ProviderFactories: map[string]func() (*schema.Provider, error){
"sysdig": func() (*schema.Provider, error) {
return sysdig.Provider(), nil
},
},
Steps: []resource.TestStep{
{
Config: awsAWSMLPolicyDataSource(rText),
},
},
})
}

func awsAWSMLPolicyDataSource(name string) string {
return fmt.Sprintf(`
resource "sysdig_secure_aws_ml_policy" "policy_1" {
name = "Test AWS ML Policy %s"
description = "Test AWS ML Policy Description %s"
enabled = true
severity = 4
rule {
description = "Test AWS ML Rule Description"
anomalous_console_login {
enabled = true
threshold = 2
severity = 1
}
}
}
data "sysdig_secure_aws_ml_policy" "policy_2" {
name = sysdig_secure_aws_ml_policy.policy_1.name
depends_on = [sysdig_secure_aws_ml_policy.policy_1]
}
`, name, name)
}
117 changes: 117 additions & 0 deletions sysdig/data_source_sysdig_secure_drift_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package sysdig

import (
"context"
"time"

v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceSysdigSecureDriftPolicy() *schema.Resource {
timeout := 5 * time.Minute

return &schema.Resource{
ReadContext: dataSourceSysdigSecureDriftPolicyRead,

Timeouts: &schema.ResourceTimeout{
Read: schema.DefaultTimeout(timeout),
},

Schema: createDriftPolicyDataSourceSchema(),
}
}

func dataSourceSysdigSecureDriftPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
return driftPolicyDataSourceRead(ctx, d, meta, "custom policy", isCustomCompositePolicy)
}

func createDriftPolicyDataSourceSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
// IMPORTANT: Type is implicit: It's automatically added upon conversion to JSON
"type": {
Type: schema.TypeString,
Computed: true,
},
"name": NameSchema(),
"description": DescriptionComputedSchema(),
"enabled": EnabledComputedSchema(),
"severity": SeverityComputedSchema(),
"scope": ScopeComputedSchema(),
"version": VersionSchema(),
"notification_channels": NotificationChannelsComputedSchema(),
"runbook": RunbookComputedSchema(),
"rule": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": ReadOnlyIntSchema(),
"name": ReadOnlyStringSchema(),
"description": DescriptionComputedSchema(),
"tags": TagsSchema(),
"version": VersionSchema(),
"enabled": BoolComputedSchema(),
"exceptions": ExceptionsComputedSchema(),
"prohibited_binaries": ExceptionsComputedSchema(),
},
},
},
"actions": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"prevent_drift": PreventActionComputedSchema(),
"container": ContainerActionComputedSchema(),
"capture": CaptureActionComputedSchema(),
},
},
},
}
}

func driftPolicyDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}, resourceName string, validationFunc func(v2.PolicyRulesComposite) bool) diag.Diagnostics {
client, err := getSecureCompositePolicyClient(meta.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

policyName := d.Get("name").(string)
policyType := policyTypeDrift

policies, _, err := client.FilterCompositePoliciesByNameAndType(ctx, policyType, policyName)
if err != nil {
return diag.FromErr(err)
}

var policy v2.PolicyRulesComposite
for _, existingPolicy := range policies {
tflog.Debug(ctx, "Filtered policies", map[string]interface{}{"name": existingPolicy.Policy.Name})

if existingPolicy.Policy.Name == policyName && existingPolicy.Policy.Type == policyType {
if !validationFunc(existingPolicy) {
return diag.Errorf("policy is not a %s", resourceName)
}
policy = existingPolicy
break
}
}

if policy.Policy == nil {
return diag.Errorf("unable to find policy %s", resourceName)
}

if policy.Policy.ID == 0 {
return diag.Errorf("unable to find %s", resourceName)
}

err = driftPolicyToResourceData(&policy, d)
if err != nil {
return diag.FromErr(err)
}

return nil
}
70 changes: 70 additions & 0 deletions sysdig/data_source_sysdig_secure_drift_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//go:build tf_acc_sysdig_secure || tf_acc_policies || tf_acc_onprem_secure

package sysdig_test

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/draios/terraform-provider-sysdig/sysdig"
)

func TestAccDriftPolicyDataSource(t *testing.T) {
rText := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
if v := os.Getenv("SYSDIG_SECURE_API_TOKEN"); v == "" {
t.Fatal("SYSDIG_SECURE_API_TOKEN must be set for acceptance tests")
}
},
ProviderFactories: map[string]func() (*schema.Provider, error){
"sysdig": func() (*schema.Provider, error) {
return sysdig.Provider(), nil
},
},
Steps: []resource.TestStep{
{
Config: driftPolicyDataSource(rText),
},
},
})
}

func driftPolicyDataSource(name string) string {
return fmt.Sprintf(`
resource "sysdig_secure_drift_policy" "policy_1" {
name = "Test Drift Policy %s"
description = "Test Drift Policy Description %s"
enabled = true
severity = 4
rule {
description = "Test Drift Rule Description"
enabled = true
exceptions {
items = ["/usr/bin/sh"]
}
prohibited_binaries {
items = ["/usr/bin/curl"]
}
}
actions {
prevent_drift = true
}
}
data "sysdig_secure_drift_policy" "policy_2" {
name = sysdig_secure_drift_policy.policy_1.name
depends_on = [sysdig_secure_drift_policy.policy_1]
}
`, name, name)
}
Loading

0 comments on commit 83ef948

Please sign in to comment.