Skip to content

Commit

Permalink
Create kms example rule (#248)
Browse files Browse the repository at this point in the history
* Create new stack name rule.

* remove unused import

* review comments

* lint error

* add new db encryption rule

* remove changes from other pr

* Update lint-and-test.yml (#247)

* Update lint-and-test.yml

* Update pyyaml dependency

* Update README.md (#246)

* Update README.md

* Add license badge

* rebase

* rebase onto master

* rebase

* make lint

* remove duplicate test

* update changelog

* add comment as for stack name rule

* make format

* rule not invoked for aurora

* make templates valid cloud formations (except for aurora one)

* make templates valid cloud formations

* Update tests/rules/test_StorageEncryptedRule.py

Co-authored-by: Ignacio Bolonio <[email protected]>

* add aurora comment

---------

Co-authored-by: Jordi Soucheiron <[email protected]>
Co-authored-by: Ignacio Bolonio <[email protected]>
  • Loading branch information
3 people authored Nov 16, 2023
1 parent 66d15bf commit d7848ba
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.

## [1.15.0]
### Additions
- New rules: `StackNameMatchesRegexRule` and `StorageEncryptedRule`
- New regex: `REGEX_ALPHANUMERICAL_OR_HYPHEN` to check if stack name only consists of alphanumerical characters and hyphens.

## [1.14.0]
### Additions
- `Config` includes a metrics logger, and it is called to register when a filter is used
Expand Down
2 changes: 2 additions & 0 deletions cfripper/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
SQSQueuePolicyPublicRule,
)
from cfripper.rules.stack_name_matches_regex import StackNameMatchesRegexRule
from cfripper.rules.storage_encrypted_rule import StorageEncryptedRule
from cfripper.rules.wildcard_policies import (
GenericResourceWildcardPolicyRule,
S3BucketPolicyWildcardActionRule,
Expand Down Expand Up @@ -88,6 +89,7 @@
S3BucketPublicReadAclRule,
S3CrossAccountTrustRule,
S3ObjectVersioningRule,
StorageEncryptedRule,
SNSTopicDangerousPolicyActionsRule,
SNSTopicPolicyNotPrincipalRule,
SNSTopicPolicyWildcardActionRule,
Expand Down
40 changes: 40 additions & 0 deletions cfripper/rules/storage_encrypted_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Dict, Optional

from pycfmodel.model.cf_model import CFModel

from cfripper.model.enums import RuleGranularity, RuleMode, RuleRisk
from cfripper.model.result import Result
from cfripper.rules.base_rules import Rule


class StorageEncryptedRule(Rule):
RULE_MODE = RuleMode.DEBUG # for demonstration purposes
RISK_VALUE = RuleRisk.LOW
REASON = (
"The database {} does not seem to be encrypted. Database resources should be encrypted and have the property "
"StorageEncrypted set to True."
)
GRANULARITY = RuleGranularity.RESOURCE

def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result:
result = Result()

for resource in cfmodel.Resources.values():
is_encrypted = getattr(resource.Properties, "StorageEncrypted", False)
db_name = getattr(resource.Properties, "DBName", "(could not get DB name)")
if (
resource.Type == "AWS::RDS::DBInstance"
and not is_encrypted
and not getattr(resource.Properties, "Engine", "").startswith(
"aurora"
) # not applicable for aurora since the encryption for DB instances is managed by the DB cluster
):

self.add_failure_to_result(
result,
self.REASON.format(db_name),
context={"config": self._config, "extras": extras},
resource_types={resource.Type},
)

return result
88 changes: 88 additions & 0 deletions tests/rules/test_StorageEncryptedRule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import pytest

from cfripper.model.enums import RuleGranularity, RuleMode, RuleRisk
from cfripper.model.result import Failure
from cfripper.rules.storage_encrypted_rule import StorageEncryptedRule
from tests.utils import get_cfmodel_from


def test_storage_encrypted_rule_valid_results():
rule = StorageEncryptedRule(None)
model = get_cfmodel_from("rules/StorageEncryptedRule/encrypted_db_resource.yml")
resolved_model = model.resolve()
result = rule.invoke(resolved_model)

assert result.valid
assert result.failures == []


def test_rule_not_failing_for_aurora():
rule = StorageEncryptedRule(None)
model = get_cfmodel_from("rules/StorageEncryptedRule/aurora_engine_used.yml")
resolved_model = model.resolve()
result = rule.invoke(resolved_model)

assert result.valid
assert result.failures == []


@pytest.mark.parametrize(
"template, failures",
[
(
"rules/StorageEncryptedRule/missing_storage_encrypted_flag.yml",
[
Failure(
granularity=RuleGranularity.RESOURCE,
reason="The database some-name does not seem to be encrypted. Database resources should be "
"encrypted and have the property StorageEncrypted set to True.",
risk_value=RuleRisk.LOW,
rule="StorageEncryptedRule",
rule_mode=RuleMode.DEBUG,
actions=None,
resource_ids=None,
resource_types={"AWS::RDS::DBInstance"},
)
],
),
(
"rules/StorageEncryptedRule/two_resources_not_encrypted.yml",
[
Failure(
granularity=RuleGranularity.RESOURCE,
reason="The database some-name does not seem to be encrypted. Database resources should be "
"encrypted and have the property StorageEncrypted set to True.",
risk_value=RuleRisk.LOW,
rule="StorageEncryptedRule",
rule_mode=RuleMode.DEBUG,
actions=None,
resource_ids=None,
resource_types={"AWS::RDS::DBInstance"},
),
Failure(
granularity=RuleGranularity.RESOURCE,
reason="The database some-name-backup does not seem to be encrypted. Database resources should be "
"encrypted and have the property StorageEncrypted set to True.",
risk_value=RuleRisk.LOW,
rule="StorageEncryptedRule",
rule_mode=RuleMode.DEBUG,
actions=None,
resource_ids=None,
resource_types={"AWS::RDS::DBInstance"},
),
],
),
(
"rules/StorageEncryptedRule/no_db_resource.yml",
[],
),
],
)
def test_add_failure_if_db_resource_not_encrypted(template, failures):
rule = StorageEncryptedRule(None)
model = get_cfmodel_from(template)
resolved_model = model.resolve()
result = rule.invoke(resolved_model)

assert result.valid
assert result.failures == failures
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Resources:
DBMaster:
Type: AWS::RDS::DBInstance
Properties:
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
DBInstanceIdentifier: !Sub ${AWS::StackName}-master
DBName: "some-name"
Engine: aurora-postgresql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
MultiAZ: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-master
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Resources:
DBMaster:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: "100"
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
BackupRetentionPeriod: 14
DBInstanceIdentifier: !Sub ${AWS::StackName}-master
DBName: "some-name"
Engine: mysql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
MultiAZ: true
StorageEncrypted: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-master
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Resources:
DBMaster:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: "100"
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
BackupRetentionPeriod: 14
DBInstanceIdentifier: !Sub ${AWS::StackName}-master
DBName: "some-name"
Engine: mysql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
MultiAZ: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-master
16 changes: 16 additions & 0 deletions tests/test_templates/rules/StorageEncryptedRule/no_db_resource.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Resources:
SomeResource:
Type: AWS::RDS::DBCluster
Properties:
AllocatedStorage: "100"
AutoMinorVersionUpgrade: true
BackupRetentionPeriod: 14
DBClusterIdentifier: !Sub ${AWS::StackName}-master
DatabaseName: "some-name"
Engine: mysql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
StorageEncrypted: false
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-master
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Resources:
DBMaster:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: "100"
AllowMajorVersionUpgrade: false
AutoMinorVersionUpgrade: true
BackupRetentionPeriod: 14
DBInstanceIdentifier: !Sub ${AWS::StackName}-master
DBName: "some-name"
Engine: mysql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
MultiAZ: true
StorageEncrypted: false
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-master
DBBackup:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: "100"
AllowMajorVersionUpgrade: true
AutoMinorVersionUpgrade: false
BackupRetentionPeriod: 7
DBInstanceIdentifier: !Sub ${AWS::StackName}-backup
DBName: "some-name-backup"
Engine: mysql
EngineVersion: "13.2"
KmsKeyId: "some-kms-key"
MultiAZ: true
StorageEncrypted: false
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-backup

0 comments on commit d7848ba

Please sign in to comment.