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

feat(cloud): add the DeletionProtection attribute to the RDS Cluster #1443

Merged
merged 5 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions avd_docs/aws/rds/AVD-AWS-0343/Terraform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

Enable deletion protection at RDS clusters

```hcl
resource "aws_rds_cluster" "good_example" {
deletion_protection = true
}

```
12 changes: 12 additions & 0 deletions avd_docs/aws/rds/AVD-AWS-0343/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

Ensure deletion protection is enabled for RDS clusters.

### Impact
<!-- Add Impact here -->

<!-- DO NOT CHANGE -->
{{ remediationActions }}

### Links
- https://docs.aws.amazon.com/config/latest/developerguide/rds-cluster-deletion-protection-enabled.html

1 change: 1 addition & 0 deletions internal/adapters/cloud/aws/rds/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ func (a *adapter) adaptCluster(dbCluster types.DBCluster) (*rds.Cluster, error)
Engine: defsecTypes.String(engine, dbClusterMetadata),
LatestRestorableTime: defsecTypes.TimeUnresolvable(dbClusterMetadata),
AvailabilityZones: availabilityZones,
DeletionProtection: defsecTypes.Bool(aws.ToBool(dbCluster.DeletionProtection), dbClusterMetadata),
}

return cluster, nil
Expand Down
1 change: 1 addition & 0 deletions internal/adapters/cloudformation/aws/rds/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func getClusters(ctx parser.FileContext) (clusters map[string]rds.Cluster) {
PublicAccess: defsecTypes.BoolDefault(false, clusterResource.Metadata()),
Engine: defsecTypes.StringDefault(rds.EngineAurora, clusterResource.Metadata()),
LatestRestorableTime: defsecTypes.TimeUnresolvable(clusterResource.Metadata()),
DeletionProtection: defsecTypes.BoolDefault(false, clusterResource.Metadata()),
}

if engineProp := clusterResource.GetProperty("Engine"); engineProp.IsString() {
Expand Down
2 changes: 2 additions & 0 deletions internal/adapters/terraform/aws/rds/adapt.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func getClusters(modules terraform.Modules) (clusters []rds.Cluster) {
PublicAccess: defsecTypes.BoolDefault(false, defsecTypes.NewUnmanagedMetadata()),
Engine: defsecTypes.StringUnresolvable(defsecTypes.NewUnmanagedMetadata()),
LatestRestorableTime: defsecTypes.TimeUnresolvable(defsecTypes.NewUnmanagedMetadata()),
DeletionProtection: defsecTypes.BoolDefault(false, defsecTypes.NewUnmanagedMetadata()),
}
for _, orphan := range orphanResources {
orphanage.Instances = append(orphanage.Instances, adaptClusterInstance(orphan, modules))
Expand Down Expand Up @@ -224,6 +225,7 @@ func adaptCluster(resource *terraform.Block, modules terraform.Modules) (rds.Clu
Engine: resource.GetAttribute("engine").AsStringValueOrDefault(rds.EngineAurora, resource),
LatestRestorableTime: defsecTypes.TimeUnresolvable(resource.GetMetadata()),
AvailabilityZones: resource.GetAttribute("availability_zones").AsStringValueSliceOrEmpty(resource),
DeletionProtection: resource.GetAttribute("deletion_protection").AsBoolValueOrDefault(false, resource),
}, ids
}

Expand Down
2 changes: 2 additions & 0 deletions internal/adapters/terraform/aws/rds/adapt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Test_Adapt(t *testing.T) {
kms_key_id = "kms_key_1"
storage_encrypted = true
replication_source_identifier = "arn-of-a-source-db-cluster"
deletion_protection = true
}

resource "aws_rds_cluster_instance" "example" {
Expand Down Expand Up @@ -121,6 +122,7 @@ func Test_Adapt(t *testing.T) {
defsecTypes.String("us-west-2b", defsecTypes.NewTestMetadata()),
defsecTypes.String("us-west-2c", defsecTypes.NewTestMetadata()),
},
DeletionProtection: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
},
},
Classic: rds.Classic{
Expand Down
1 change: 1 addition & 0 deletions pkg/providers/aws/rds/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Cluster struct {
Engine defsecTypes.StringValue
LatestRestorableTime defsecTypes.TimeValue
AvailabilityZones []defsecTypes.StringValue
DeletionProtection defsecTypes.BoolValue
}

type Snapshots struct {
Expand Down
4 changes: 4 additions & 0 deletions pkg/rego/schemas/cloud.json
Original file line number Diff line number Diff line change
Expand Up @@ -2390,6 +2390,10 @@
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.types.IntValue"
},
"deletionprotection": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.types.BoolValue"
},
"encryption": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.providers.aws.rds.Encryption"
Expand Down
3 changes: 3 additions & 0 deletions rules/cloud/policies/aws/iam/filter_iam_pass_role_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ test_with_allow_iam_pass_role {
"name": "policy_with_iam_pass_role",
"document": {"value": "{\"Version\":\"2012-10-17\",\"Id\":\"\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{},\"NotPrincipal\":{},\"Action\":[\"iam:PassRole\"],\"NotAction\":null,\"Resource\":[\"arn:aws:iam::193063503752:role/atc-node\"],\"NotResource\":null,\"Condition\":{}}]}"},
}]

r := deny with input as {"aws": {"iam": {"policies": policies}}}
count(r) == 1
}
Expand All @@ -14,6 +15,7 @@ test_with_deny_iam_pass_role {
"name": "policy_with_iam_pass_role",
"document": {"value": "{\"Version\":\"2012-10-17\",\"Id\":\"\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Deny\",\"Principal\":{},\"NotPrincipal\":{},\"Action\":[\"iam:PassRole\"],\"NotAction\":null,\"Resource\":[\"arn:aws:iam::193063503752:role/atc-node\"],\"NotResource\":null,\"Condition\":{}}]}"},
}]

r := deny with input as {"aws": {"iam": {"policies": policies}}}
count(r) == 0
}
Expand All @@ -23,6 +25,7 @@ test_with_no_iam_pass_role {
"name": "policy_without_iam_pass_role",
"document": {"value": "{\"Version\":\"2012-10-17\",\"Id\":\"\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{},\"NotPrincipal\":{},\"Action\":[\"s3:GetObject\"],\"NotAction\":null,\"Resource\":[\"arn:aws:s3:::examplebucket/*\"],\"NotResource\":null,\"Condition\":{}}]}"},
}]

r := deny with input as {"aws": {"iam": {"policies": policies}}}
count(r) == 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# METADATA
# title: "RDS Cluster Deletion Protection Disabled"
# description: "Ensure deletion protection is enabled for RDS clusters."
# scope: package
# schemas:
# - input: schema["cloud"]
# related_resources:
# - https://docs.aws.amazon.com/config/latest/developerguide/rds-cluster-deletion-protection-enabled.html
# custom:
# avd_id: AVD-AWS-0343
# provider: aws
# service: rds
# severity: MEDIUM
# short_code: enable-cluster-deletion-protection
# recommended_action: "Modify the RDS clusters to enable deletion protection."
# input:
# selector:
# - type: cloud
# subtypes:
# - service: rds
# provider: aws
package builtin.aws.rds.aws0343

deny[res] {
cluster := input.aws.rds.clusters[_]
not cluster.deletionprotection.value
res := result.new("Cluster does not have Deletion Protection enabled", cluster.deletionprotection)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package builtin.aws.rds.aws0343

test_detects_when_disabled {
r := deny with input as {"aws": {"rds": {"clusters": [{"deletionprotection": {"value": false}}]}}}
count(r) == 1
}

test_when_enabled {
r := deny with input as {"aws": {"rds": {"clusters": [{"deletionprotection": {"value": true}}]}}}
count(r) == 0
}