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

Create kms example rule #248

Merged
merged 23 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
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