Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SA-535]Add support for security monitoring rules #763

Merged
merged 22 commits into from
Dec 3, 2020

Conversation

greentruff
Copy link
Contributor

@greentruff greentruff commented Nov 24, 2020

Add support for Security Monitoring public API.
See: https://docs.datadoghq.com/api/v2/security-monitoring/

Closes #509

Comment on lines +33 to +43
"default_only_filter": {
Type: schema.TypeBool,
Optional: true,
Description: "Limit the search to default rules",
},
"user_only_filter": {
Type: schema.TypeBool,
Optional: true,
Description: "Limit the search to user rules",
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two different attributes are used for boolean values to work around GetOk only checking if there was truthy value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that because you don't check the value returned? GetOk is weird some time, but I'm not sure it's warranted here. AFAICT we only end up with one boolean, which you should be able to achieve with one field.

Copy link
Contributor Author

@greentruff greentruff Nov 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the resource to have a default value. Unless I am missing something, in this case though it will not be possible to filter out user rules only. Will we get d.GetOk("default_only_filter") == false, false both in the case where the attribute is not present and in the case where the attribute is default_only_filter = false.

(There are discussions about whether we should remove the datasource so leaving it as is for now).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, because you use nil as a possible value, so there are effectively 3 states, not just 2.

return nil
}

func computeSecMonDataSourceRulesId(nameFilter *string, defaultFilter *bool, tagFilter map[string]bool) string {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a costly way to get a hash, I am not sure if this matters

@greentruff greentruff marked this pull request as ready for review November 25, 2020 18:07
@greentruff greentruff requested a review from a team as a code owner November 25, 2020 18:07
Copy link
Contributor

@therve therve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Some minor question/remarks, plus need clarifications around boolean usage. Need to check why CI is red too.

Comment on lines +33 to +43
"default_only_filter": {
Type: schema.TypeBool,
Optional: true,
Description: "Limit the search to default rules",
},
"user_only_filter": {
Type: schema.TypeBool,
Optional: true,
Description: "Limit the search to user rules",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that because you don't check the value returned? GetOk is weird some time, but I'm not sure it's warranted here. AFAICT we only end up with one boolean, which you should be able to achieve with one field.

docs/resources/security_monitoring_rule.md.md Outdated Show resolved Hide resolved
docs/resources/security_monitoring_rule.md.md Outdated Show resolved Hide resolved
Copy link
Contributor

@Bit-Doctor Bit-Doctor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a first pass at custom security rules.
I'll check default rule resource and data source in a bit.

So far looks good with a few nits.

datadog/resource_datadog_security_monitoring_rule.go Outdated Show resolved Hide resolved
datadog/resource_datadog_security_monitoring_rule_test.go Outdated Show resolved Hide resolved
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think name is required when there's more than one case.

Unfortunately I looked and it seems it's not possible to have that checked dynamically except maybe add a ValidateFunc to the schema.
But I might be better to let the API fail. The only drawback is that the user will know it at apply time.

Might be worth adding a note in the description?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a trade-off between client-side validation and server-side validation. I think it easier and more maintenable to delegate most of it to the server, but we have to make sure the error message is descriptive and visible.

@btoueg btoueg changed the title Add support for security monitoring rules [SA-535]Add support for security monitoring rules Nov 26, 2020
Comment on lines 45 to 50
# Create a resource for each default rule id and enable the rule
resource "datadog_security_monitoring_default_rule" "attack_rule" {
for_each = toset(data.datadog_security_monitoring_rules.rules.rule_ids)
rule_id = each.key
enabled = true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this is the motivation behind datadog_security_rules datasource. It is a peculiar use of datasources AFAICT. For instance, how does it behave if you create datasources that overlaps? Say you have attack_default_rules and specific_framework rules. A rule could belong to both, and would result in multiple datadog_security_monitoring_default_rule. My guess is that in this case, last write wins. but with lots of subtleties. Are we comfortable to recommend such usage?

I would have recommended dealing with default rules with a prior import.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC your edge case is something like this ?

resource "datadog_security_monitoring_default_rule" "rule1" {
    rule_id = "SAMEID"
    enabled = true
}

resource "datadog_security_monitoring_default_rule" "rule2" {
    rule_id = "SAMEID"
    enabled = false
}

I'm actually unsure what whould Terraform do here, It will probably conflict on the resource ID depending on how you build it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am asking @NicolasLevaique what he thinks about this as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I just thought of a resource in the AWS provider that has a similar behavior, with no real create/delete.
So at least this behavior exist.

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_vpc
https://github.com/hashicorp/terraform-provider-aws/blob/master/aws/resource_aws_default_vpc.go

Might be worth checking if they handle this edge case with two resource with different params.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe a bit late but there's setintersection, setunion and setsubtract functions that could have helped user with that issue.

@greentruff greentruff force-pushed the atrufanow/add-signal branch from 9e58eb2 to b015a85 Compare December 1, 2020 18:24
@greentruff greentruff force-pushed the atrufanow/add-signal branch from b015a85 to fff4df2 Compare December 2, 2020 10:06
@greentruff
Copy link
Contributor Author

@therve Can you take another look? After discussing with @NicolasLevaique, I changed the default rule so that it needs to be imported like other resources. I kept the datasource though since it can be useful and I don't know how to test the default rule without it.
There is still the question of the default_only_filter/user_only_filter in the datasource: in this case there are three valid states, no filter or filter by user/default rules. Merging the attributes would mean that we would have to always return either default or user rules. Do you think it is ok to leave both? The alternative is to change the format to something like

filter {
"is_default_rule": true,
}

but this would not be consistent with other datasources in the provider.

@therve
Copy link
Contributor

therve commented Dec 2, 2020

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@therve
Copy link
Contributor

therve commented Dec 3, 2020

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@therve therve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor comments, otherwise almost there.


if shouldUpdate {
_, _, err := datadogClientV2.SecurityMonitoringApi.UpdateSecurityMonitoringRule(authV2, ruleId).Body(*ruleUpdate).Execute()
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should inline that if with the previous command.


func resourceDatadogSecurityMonitoringRule() *schema.Resource {
return &schema.Resource{
Exists: resourceDatadogSecurityMonitoringRuleExists,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should remove the exists, and check for 404 in read instead (and set the id to "" when you get a 404)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the last commit, can you double check how I did it though? I didn't find any other resources which omit exists.


_, err := datadogClientV2.SecurityMonitoringApi.DeleteSecurityMonitoringRule(authV2, d.Id()).Execute()

if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can inline that condition too.

```

```
terraform import datadog_security_monitoring_default_rule.adefaultrule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're missing an ID here.

- Remove exists for secmon rule resource
- Inline if err != nil checks
- Add missing id in documentation
if err != nil {
if httpResponse != nil && httpResponse.StatusCode == 404 {
d.SetId("")
return errors.New("security monitoring rule does not exist")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost that, just return nil in that case instead of an error. Thanks.

@therve therve merged commit 49fa690 into master Dec 3, 2020
@therve therve deleted the atrufanow/add-signal branch December 3, 2020 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Support security detection rules
4 participants