Detection rules › Panther

Sensitive AWS CloudWatch Log Encryption

Severity
high
Tags
AWS, Configuration Required, Panther
Reference
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html
Source
github.com/panther-labs/panther-analysis

AWS automatically performs server-side encryption of logs, but you can encrypt with your own CMK to protect extra sensitive log data.

Rule body yaml

AnalysisType: policy
Filename: aws_cloudwatch_loggroup_sensitive_encrypted.py
PolicyID: "AWS.CloudWatchLogs.SensitiveLogGroup.Encryption"
DisplayName: "Sensitive AWS CloudWatch Log Encryption"
Enabled: false
ResourceTypes:
  - AWS.CloudWatch.LogGroup
Tags:
  - AWS
  - Configuration Required
  - Panther
Severity: High
Description: >
  AWS automatically performs server-side encryption of logs, but you can encrypt with your own CMK
  to protect extra sensitive log data.
Runbook: >
  Encrypt the CloudWatch log group with a KMS key, or add this log group to the ignore list (SENSITIVE_LOG_GROUP_ARN_REGEXS).
Reference: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html
Tests:
  - Name: Logs Are Encrypted
    ExpectedResult: true
    Resource:
      {
        "ARN": "arn:aws:logs:us-west-2:1234456789011:log-group:LogGroup-2",
        "AccountId": "123456789012",
        "Arn": "arn:aws:logs:us-west-2:1234456789012:log-group:LogGroup-2",
        "CreationTime": 1234567890123,
        "KmsKeyId": "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012",
        "LogGroupName": "LogGroup-2",
        "MetricFilterCount": 0,
        "Name": "LogGroup-2",
        "Region": "us-west-2",
        "ResourceID": "arn:aws:logs:us-west-2:1234456789012:log-group:LogGroup-2",
        "ResourceType": "AWS.CloudWatch.LogGroup",
        "RetentionInDays": null,
        "StoredBytes": 0,
        "Tags": { "Key1Name": "Value1" },
        "TimeCreated": "2009-02-13T15:31:30.000-08:00",
      }
  - Name: Sensitive Logs Are Not Encrypted
    ExpectedResult: false
    Resource:
      {
        "ARN": "arn:aws:logs:us-west-2:1234456789012:log-group:LogGroup-2",
        "AccountId": "123456789012",
        "Arn": "arn:aws:logs:us-west-2:1234456789012:log-group:LogGroup-2",
        "CreationTime": 1234567890123,
        "KmsKeyId": null,
        "LogGroupName": "LogGroup-2",
        "MetricFilterCount": 0,
        "Name": "LogGroup-2",
        "Region": "us-west-2",
        "ResourceID": "arn:aws:logs:us-west-2:1234456789012:log-group:LogGroup-2",
        "ResourceType": "AWS.CloudWatch.LogGroup",
        "RetentionInDays": null,
        "StoredBytes": 0,
        "Tags": { "Key1Name": "Value1" },
        "TimeCreated": "2009-02-13T15:31:30.000-08:00",
      }
  - Name: Non-sensitive Logs Are Not Encrypted
    ExpectedResult: true
    Resource:
      {
        "ARN": "arn:aws:logs:us-west-2:1234456789012:log-group:SomeNoneEncryptedLog",
        "AccountId": "123456789012",
        "Arn": "arn:aws:logs:us-west-2:1234456789012:log-group:SomeNoneEncryptedLog",
        "CreationTime": 1234567890123,
        "KmsKeyId": null,
        "LogGroupName": "SomeNoneEncryptedLog",
        "MetricFilterCount": 0,
        "Name": "SomeNonEncryptedLog",
        "Region": "us-west-2",
        "ResourceID": "arn:aws:logs:us-west-2:1234456789012:log-group:SomeNoneEncryptedLog",
        "ResourceType": "AWS.CloudWatch.LogGroup",
        "RetentionInDays": null,
        "StoredBytes": 0,
        "Tags": { "Key1Name": "Value1" },
        "TimeCreated": "2009-02-13T15:31:30.000-08:00",
      }

Detection logic

Rule logic imperative Python

from fnmatch import fnmatch
SENSITIVE_LOG_GROUP_ARN_REGEXS = {"*LogGroup-2*"}
def policy(resource):
    if resource["KmsKeyId"] is None:
        if SENSITIVE_LOG_GROUP_ARN_REGEXS and any(
            fnmatch(resource.get("Arn"), group_arn) for group_arn in SENSITIVE_LOG_GROUP_ARN_REGEXS
        ):
            return False
    return True

The parser cannot express this rule's logic as a field filter; the imperative Python above is the detection.