Skip to content

aalladin/terraform-aws-cloudtrail-to-slack

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FivexL

Terraform module to deploy lambda that sends notifications about AWS CloudTrail events to Slack

Why this module?

This module allows you to get notifications about:

  • actions performed by root account (According to AWS best practices, you should use root account as little as possible and use SSO or IAM users)
  • API calls that failed due to lack of permissions to do so (could be an indication of compromise or misconfiguration of your services/applications)
  • console logins without MFA (Always use MFA for you IAM users or SSO)
  • track a list of events that you might consider sensitive. Think IAM changes, network changes, data storage (S3, DBs) access changes. Though we recommend keeping that to a minimum to avoid alert fatigue
  • define sophisticated rules to track user-defined conditions that are not covered by default rules (see examples below)
  • send notifications to different Slack channels based on event account id

Example message

Example message

Delivery delays

The current implementation built upon parsing of S3 notifications, and thus you should expect a 5 to 10 min lag between action and event notification in Slack. If you do not get a notification at all - check CloudWatch logs for the lambda to see if there is any issue with provided filters.

How to

Module deployment with the default ruleset

# we recomend storing hook url in SSM Parameter store and not commit it to the repo
data "aws_ssm_parameter" "hook" {
  name = "/cloudtrail-to-slack/hook"
}

module "cloudtrail_to_slack" {
  source                         = "fivexl/cloudtrail-to-slack/aws"
  version                        = "2.0.0"
  default_slack_hook_url         = data.aws_ssm_parameter.hook.value
  cloudtrail_logs_s3_bucket_name = aws_s3_bucket.cloudtrail.id
}

resource "aws_cloudtrail" "main" {
  name           = "main"
  s3_bucket_name = aws_s3_bucket.cloudtrail.id
  ...
}

resource "aws_s3_bucket" "cloudtrail" {
  ....
}

Module deployment with the default ruleset and different slack channels for different accounts

# we recomend storing hook url in SSM Parameter store and not commit it to the repo
data "aws_ssm_parameter" "default_hook" {
  name = "/cloudtrail-to-slack/default_hook"
}

data "aws_ssm_parameter" "dev_hook" {
  name = "/cloudtrail-to-slack/dev_hook"
}

data "aws_ssm_parameter" "prod_hook" {
  name = "/cloudtrail-to-slack/prod_hook"
}

module "cloudtrail_to_slack" {
  source                         = "fivexl/cloudtrail-to-slack/aws"
  version                        = "2.0.0"
  default_slack_hook_url         = data.aws_ssm_parameter.default_hook.value

  configuration = [
    {
      "accounts": ["123456789"],
      "slack_hook_url": data.aws_ssm_parameter.dev_hook.value
    },
    {
      "accounts": ["987654321"],
      "slack_hook_url": data.aws_ssm_parameter.prod_hook.value
    }
  ]

  cloudtrail_logs_s3_bucket_name = aws_s3_bucket.cloudtrail.id
}

resource "aws_cloudtrail" "main" {
  name           = "main"
  s3_bucket_name = aws_s3_bucket.cloudtrail.id
  ...
}

resource "aws_s3_bucket" "cloudtrail" {
  ....
}

Module deployment with the list of events to track and default rule sets

# we recomend storing hook url in SSM Parameter store and not commit it to the repo
data "aws_ssm_parameter" "hook" {
  name = "/cloudtrail-to-slack/hook"
}

locals {
  # CloudTrail events
  cloudtrail = "DeleteTrail,StopLogging,UpdateTrail"
  # EC2 Instance connect and EC2 events
  ec2 = "SendSSHPublicKey"
  # Config
  config = "DeleteConfigRule,DeleteConfigurationRecorder,DeleteDeliveryChannel,DeleteEvaluationResults"
  # All events
  events_to_track = "${local.cloudtrail},${local.ec2},${local.config}"
}

module "cloudtrail_to_slack" {
  source                         = "fivexl/cloudtrail-to-slack/aws"
  version                        = "2.0.0"
  default_slack_hook_url         = data.aws_ssm_parameter.hook.value
  cloudtrail_logs_s3_bucket_name = aws_s3_bucket.cloudtrail.id
  events_to_track                = local.events_to_track
}

resource "aws_cloudtrail" "main" {
  name           = "main"
  s3_bucket_name = aws_s3_bucket.cloudtrail.id
  ...
}

resource "aws_s3_bucket" "cloudtrail" {
  ....
}

Module deployment with user-defined rules, list of events to track, and default rule sets

# we recomend storing hook url in SSM Parameter store and not commit it to the repo
data "aws_ssm_parameter" "hook" {
  name = "/cloudtrail-to-slack/hook"
}

module "cloudtrail_to_slack" {
  source                         = "fivexl/cloudtrail-to-slack/aws"
  version                        = "2.0.0"
  default_slack_hook_url         = data.aws_ssm_parameter.hook.value
  cloudtrail_logs_s3_bucket_name = aws_s3_bucket.cloudtrail.id
  rules                          = "'errorCode' in event and event['errorCode'] == 'UnauthorizedOperation','userIdentity.type' in event and event['userIdentity.type'] == 'Root'"
  events_to_track                = "CreateUser,StartInstances"
}

Catch SSM Session events for the "111111111" account

locals {
  cloudtrail_rules = [
      "'userIdentity.accountId' in event and event['userIdentity.accountId'] == '11111111111' and event['eventSource'] == 'ssm.amazonaws.com' and event['eventName'].endswith(('Session'))",
    ]
}

# we recomend storing hook url in SSM Parameter store and not commit it to the repo
data "aws_ssm_parameter" "hook" {
  name = "/cloudtrail-to-slack/hook"
}

module "cloudtrail_to_slack" {
  source                         = "fivexl/cloudtrail-to-slack/aws"
  version                        = "2.0.0"
  default_slack_hook_url         = data.aws_ssm_parameter.hook.value
  cloudtrail_logs_s3_bucket_name = aws_s3_bucket.cloudtrail.id
  rules                          = join(",", local.cloudtrail_rules)
}

About rules and how they are applied

This module comes with a set of predefined rules (default rules) that users can take advantage of. Rules are python strings that are evaluated in the runtime and should return the bool value. CloudTrail event (see format here) is flattened before processing and should be referenced as event variable So, for instance, to access ARN from the event below, you should use the notation userIdentity.arn

{
  "eventVersion": "1.05",
  "userIdentity": {
    "type": "IAMUser",
    "principalId": "XXXXXXXXXXX",
    "arn": "arn:aws:iam::XXXXXXXXXXX:user/xxxxxxxx",
    "accountId": "XXXXXXXXXXX",
    "userName": "xxxxxxxx"
  },
  "eventTime": "2019-07-03T16:14:51Z",
  "eventSource": "signin.amazonaws.com",
  "eventName": "ConsoleLogin",
  "awsRegion": "us-east-1",
  "sourceIPAddress": "83.41.208.104",
  "userAgent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0",
  "requestParameters": null,
  "responseElements": {
    "ConsoleLogin": "Success"
  },
  "additionalEventData": {
    "LoginTo": "https://console.aws.amazon.com/ec2/v2/home?XXXXXXXXXXX",
    "MobileVersion": "No",
    "MFAUsed": "No"
  },
  "eventID": "0e4d136e-25d4-4d92-b2b2-8a9fe1e3f1af",
  "eventType": "AwsConsoleSignIn",
  "recipientAccountId": "XXXXXXXXXXX"
}

Default rules

# Notify if someone logged in without MFA but skip notification for SSO logins
default_rules.append('event["eventName"] == "ConsoleLogin" ' +
                     'and event["additionalEventData.MFAUsed"] != "Yes" ' +
                     'and "assumed-role/AWSReservedSSO" not in event.get("userIdentity.arn", "")')

# Notify if someone is trying to do something they not supposed to be doing but do not notify
# about not logged in actions since there are a lot of scans for open buckets that generate noise
# This is useful to discover any misconfigurations in your account. Time to time services will try
# to do something but fail due to IAM permissions and those errors are very hard to find using
# other means
default_rules.append('event.get("errorCode", "") == "UnauthorizedOperation"')
default_rules.append('event.get("errorCode", "") == "AccessDenied" ' +
                     'and (event.get("userIdentity.accountId", "") != "ANONYMOUS_PRINCIPAL")')

# Notify about all non-read actions done by root
default_rules.append('event.get("userIdentity.type", "") == "Root" ' +
                     'and not event["eventName"].startswith(("Get", "List", "Describe", "Head"))')

License

Apache 2 Licensed. See LICENSE for full details.

Requirements

Name Version
terraform >= 0.12.31
aws >= 3.43

Providers

Name Version
aws 3.62.0

Modules

Name Source Version
lambda terraform-aws-modules/lambda/aws 2.22.0

Resources

Name Type
aws_lambda_permission.s3 resource
aws_s3_bucket_notification.bucket_notification resource
aws_iam_policy_document.s3 data source
aws_s3_bucket.cloudtrail data source

Inputs

Name Description Type Default Required
cloudtrail_logs_s3_bucket_name Name of the CloudWatch log s3 bucket that contains CloudTrail events string n/a yes
configuration Allows to configure slack web hook url per account(s) so you can separate events from different accounts to different channels. Useful in context of AWS organization
list(object({
accounts = list(string)
slack_hook_url = string
}))
null no
dead_letter_target_arn The ARN of an SNS topic or SQS queue to notify when an invocation fails. string null no
default_slack_hook_url Slack incoming webhook URL to be used if AWS account id does not match any account id from configuration variable string n/a yes
events_to_track Comma-separated list events to track and report string "" no
function_name Lambda function name string "fivexl-cloudtrail-to-slack" no
rules Comma-separated list of rules to track events if just event name is not enough string "" no
tags Tags to attach to resources map(string) {} no
use_default_rules Should default rules be used bool true no

Outputs

Name Description
lambda_function_arn The ARN of the Lambda Function

About

Parse AWS CloudTrail events and send alerts to Slack for events that match pre-configured rules

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 75.4%
  • HCL 23.4%
  • Shell 1.2%