diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_federated.json b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_federated.json new file mode 100644 index 000000000..6e7f1731d --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_federated.json @@ -0,0 +1,68 @@ +{ + "version": "0", + "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "detail-type": "AWS API Call via CloudTrail", + "source": "aws.ec2", + "account": "123456789012", + "time": "2018-07-22T09:49:51Z", + "region": "ap-southeast-2", + "resources": [], + "detail": { + "eventVersion": "1.05", + "userIdentity": { + "type": "AssumedRole", + "principalId": "xxxxxxxxxxxxxxx:john.doh@example.com", + "arn": "arn:aws:sts::xxxxxxxxxxxxxxx:assumed-role/xxxxxxxxxxxxxxx/john.doh@example.com", + "accountId": "xxxxxxxxxxxxxxx", + "accessKeyId": "xxxxxxxxxxxxxxx", + "sessionContext": { + "attributes": { + "mfaAuthenticated": "false", + "creationDate": "2018-07-22T09:49:51Z" + }, + "sessionIssuer": { + "type": "Role", + "principalId": "xxxxxxxxxxxxxxx", + "arn": "arn:aws:iam::123456789012:role/xxxxxxxxxxxxxxx", + "accountId": "123456789012", + "userName": "xxxxxxxxxxxxxxx" + } + } + }, + "eventTime": "2018-07-22T09:49:51Z", + "eventSource": "ec2.amazonaws.com", + "eventName": "AuthorizeSecurityGroupIngress", + "awsRegion": "ap-southeast-2", + "sourceIPAddress": "xxxxxxxxxxxxxxx", + "userAgent": "console.ec2.amazonaws.com", + "requestParameters": { + "groupId": "sg-xxxxxxxxxxxxxx", + "ipPermissions": { + "items": [ + { + "ipProtocol": "tcp", + "fromPort": 22, + "toPort": 22, + "groups": {}, + "ipRanges": { + "items": [ + { + "cidrIp": "0.0.0.0/0" + } + ] + }, + "ipv6Ranges": {}, + "prefixListIds": {} + } + ] + } + }, + "responseElements": { + "_return": true + }, + "requestID": "xxxxxxxxxxxxxxx", + "eventID": "xxxxxxxxxxxxxxx", + "eventType": "AwsApiCall", + "recipientAccountId": "xxxxxxxxxxxxxxx" + } +} \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_iam.json b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_iam.json new file mode 100644 index 000000000..59faaece0 --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_iam.json @@ -0,0 +1,64 @@ +{ + "version": "0", + "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "detail-type": "AWS API Call via CloudTrail", + "source": "aws.ec2", + "account": "123456789012", + "time": "2018-07-22T09:49:51Z", + "region": "ap-southeast-2", + "resources": [], + "detail": { + "eventVersion": "1.05", + "userIdentity": { + "type": "IAMUser", + "principalId": "XXXXXXXXXXXXXXXXXXX", + "arn": "arn:aws:iam::123456789012:user/john.doh", + "accountId": "123456789012", + "accessKeyId": "XXXXXXXXXXXXXXXXXXX", + "userName": "john.doh", + "sessionContext": { + "attributes": { + "mfaAuthenticated": "false", + "creationDate": "2018-07-22T02:00:21Z" + } + }, + "invokedBy": "signin.amazonaws.com" + }, + "eventTime": "2018-07-22T09:49:51Z", + "eventSource": "ec2.amazonaws.com", + "eventName": "AuthorizeSecurityGroupIngress", + "awsRegion": "ap-southeast-2", + "sourceIPAddress": "192.168.1.1", + "userAgent": "signin.amazonaws.com", + "requestParameters": { + "groupId": "sg-xxxxxxxxxxxxxxx", + "ipPermissions": { + "items": [ + { + "ipProtocol": "tcp", + "fromPort": 22, + "toPort": 22, + "groups": {}, + "ipRanges": { + "items": [ + { + "cidrIp": "0.0.0.0/0", + "description": "to lazy to put correct ip, lets give everyone access" + } + ] + }, + "ipv6Ranges": {}, + "prefixListIds": {} + } + ] + } + }, + "responseElements": { + "requestId": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "_return": true + }, + "requestID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "eventID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "eventType": "AwsApiCall" + } +} \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_security_group.json b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_security_group.json new file mode 100644 index 000000000..1ea839419 --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/events/event_security_group.json @@ -0,0 +1,64 @@ +{ + "version": "0", + "id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "detail-type": "AWS API Call via CloudTrail", + "source": "aws.ec2", + "account": "123456789012", + "time": "2018-07-28T09:17:52Z", + "region": "ap-southeast-2", + "resources": [], + "detail": { + "eventVersion": "1.05", + "userIdentity": { + "type": "IAMUser", + "principalId": "XXXXXXXXXXXXXXXXXXX", + "arn": "arn:aws:iam::123456789012:user/john.doh", + "accountId": "123456789012", + "accessKeyId": "XXXXXXXXXXXXXXXXXXX", + "userName": "john.doh", + "sessionContext": { + "attributes": { + "mfaAuthenticated": "false", + "creationDate": "2018-07-28T02:20:35Z" + } + }, + "invokedBy": "signin.amazonaws.com" + }, + "eventTime": "2018-07-28T09:17:52Z", + "eventSource": "ec2.amazonaws.com", + "eventName": "AuthorizeSecurityGroupIngress", + "awsRegion": "ap-southeast-2", + "sourceIPAddress": "192.168.1.10", + "userAgent": "signin.amazonaws.com", + "requestParameters": { + "groupId": "sg-xxxxxxxxxxxxxxx", + "ipPermissions": { + "items": [ + { + "ipProtocol": "tcp", + "fromPort": 3389, + "toPort": 3389, + "groups": { + "items": [ + { + "groupId": "sg-zzzzzzzzzzzzzzz", + "description": "All RDP from front-end security group" + } + ] + }, + "ipRanges": {}, + "ipv6Ranges": {}, + "prefixListIds": {} + } + ] + } + }, + "responseElements": { + "requestId": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "_return": true + }, + "requestID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "eventID": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "eventType": "AwsApiCall" + } +} \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/template.yaml b/examples/2016-10-31/cloudwatch-event-to-msteams/template.yaml new file mode 100644 index 000000000..6120d7346 --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/template.yaml @@ -0,0 +1,62 @@ +# To deploy with SAM: +# download and install sam: https://github.com/awslabs/aws-sam-cli +# sam package --template-file template.yaml --s3-bucket your-s3-bucket --output-template sam-output.yaml +# sam deploy --template-file sam-output.yaml --stack-name WatchSecurityGroup --capabilities CAPABILITY_IAM + +# To test locally: (make sure to modifify the "groupId" with a real security group, add your webhook into vars.json) + # sam local invoke -e events/event_iam.json WatchSecurityGroupFunction -n vars.json + +Transform: AWS::Serverless-2016-10-31 +Description: Lambda function to watch EC2 Security Group Events to Send to Teams + +Parameters: + + WebHook: + Description: MS Teams Webhook + Type: String + Default: https://outlook.office.com/webhook/your/webhook/etc + +Resources: + + # Lambda Function + WatchSecurityGroupFunction: + Type: AWS::Serverless::Function + Properties: + Handler: watch-security-group.lambda_handler + Timeout: 10 + Tracing: Active + MemorySize: 128 + Runtime: python3.6 + CodeUri: . + Description: Detects EC2 Security Group Events to Send to Teams + Policies: + - AWSLambdaVPCAccessExecutionRole + - CloudWatchLogsFullAccess + - Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - ec2:DescribeSecurityGroupReferences + - ec2:DescribeSecurityGroups + - ec2:DescribeStaleSecurityGroups + Resource: '*' + Events: + WatchSecurityGroupRule: + Type: CloudWatchEvent + Description: Detects EC2 Security Group Events to Send to Teams + Properties: + Pattern: + source: + - "aws.ec2" + detail-type: + - "AWS API Call via CloudTrail" + detail: + eventSource: + - "ec2.amazonaws.com" + eventName: + - "AuthorizeSecurityGroupIngress" + - "RevokeSecurityGroupIngress" + Environment: + Variables: + TEAM_WEBHOOK: !Ref WebHook + diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/vars.json b/examples/2016-10-31/cloudwatch-event-to-msteams/vars.json new file mode 100644 index 000000000..f354f8109 --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/vars.json @@ -0,0 +1,5 @@ +{ + "WatchSecurityGroupFunction": { + "TEAM_WEBHOOK": "https://outlook.office.com/webhook/your/webhook/etc" + } +} \ No newline at end of file diff --git a/examples/2016-10-31/cloudwatch-event-to-msteams/watch-security-group.py b/examples/2016-10-31/cloudwatch-event-to-msteams/watch-security-group.py new file mode 100644 index 000000000..245a3b73b --- /dev/null +++ b/examples/2016-10-31/cloudwatch-event-to-msteams/watch-security-group.py @@ -0,0 +1,167 @@ +# To test locally: (make sure to modifify the "groupId" with a real security group, add your webhook into vars.json) +# sam local invoke -e events/event_iam.json WatchSecurityGroupFunction -n vars.json + +import boto3 +import json +import os +from botocore.vendored import requests + +# Boto3 for security group: https://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.SecurityGroup +ec2 = boto3.resource('ec2') + +# Environment Variable for Lambda +TEAM_WEBHOOK = os.environ['TEAM_WEBHOOK'] + +def get_security_group(event): + + print("--------- Event Received ---------") + print(json.dumps(event)) + print("--------- End of Event ---------") + + # Read event from something change security groups in console. + if len(event["detail"]["requestParameters"]["ipPermissions"]) > 0: + for rule in event["detail"]["requestParameters"]["ipPermissions"]["items"]: + + # Use protocol as port if fromPort is empty + from_port = rule.get("fromPort", rule["ipProtocol"]) + + # Check for multi ip address added as source + if len(rule["ipRanges"]) > 0: + + for i in rule["ipRanges"]["items"]: + + # Check if CIDR has been entered or Another Security Group + try: + cidr_address = i["cidrIp"] + except KeyError: + cidr_address = i["groupId"] + + # Get description of individual rule + description = i.get("description", "") + + # Send Message + lookup_security_group(event, from_port, cidr_address, description) + + # Check for multi security groups added as source + if len(rule["groups"]) > 0: + + for i in rule["groups"]["items"]: + + # Check if CIDR has been entered or Another Security Group + try: + cidr_address = i["cidrIp"] + except KeyError: + cidr_address = i["groupId"] + + # Get description of individual rule + description = i.get("description", "") + + # Send Message + lookup_security_group(event, from_port, cidr_address, description) + + # Event isn't coming from Console, use API event + else: + + # Check if CIDR has been entered or Another Security Group + try: + cidr_address = event["detail"]["requestParameters"]["cidrIp"] + except KeyError: + cidr_address = event["detail"]["requestParameters"]["groupId"] + + # Get Description and Port + description = event["detail"]["requestParameters"].get("description", "") + from_port = event["detail"]["requestParameters"].get("from_port", -1) + + # Send Message + lookup_security_group(event, from_port, cidr_address, description) + + +def lookup_security_group(event, source_port, cidr_address, description): + + # Look up security group attributes from group_id + try: + lookup_sg_id = event["detail"]["requestParameters"]["groupId"] + lookup_sg = ec2.SecurityGroup(lookup_sg_id) + lookup_sg_name = lookup_sg.group_name + lookup_sg_description = lookup_sg.description + + # Get Account and User Details + event_type = event["detail"]["eventName"] + aws_account = event["account"] + user_id = event["detail"]["userIdentity"]["arn"] + full_username = user_id.split(':')[-1] + + # Colors for Microsoft Teams: Blue = Add, Red = Remove + if event_type == "AuthorizeSecurityGroupIngress": + message_color = "0072C6" + else: + message_color = "FF0000" + + # Send Teams Message + create_json_message(TEAM_WEBHOOK, full_username, lookup_sg_id, lookup_sg_name, lookup_sg_description, aws_account, source_port, cidr_address, description, event_type, message_color) + + except Exception as error: + print("Error getting Security Group from Event:") + print(f"{error}") + + +def create_json_message(TEAM_WEBHOOK, full_username, lookup_sg_id, lookup_sg_name, lookup_sg_description, aws_account, source_port, cidr_address, description, event_type, message_color): + + body = { + "@context": "http://schema.org/extensions", + "@type": "MessageCard", + "themeColor": f"{message_color}", + "title": "Security Group Watcher", + "text": " ", + "sections": [ + { + "activityText": f"Security group **{lookup_sg_id}** has been modified and trigger this alert" + }, + { + "facts": [ + { + "name": "Action", + "value": f"{event_type}" + }, + { + "name": "User", + "value": f"{full_username}" + }, + { + "name": "Account", + "value": f"{aws_account}" + }, + { + "name": "SourcePort", + "value": f"{source_port}" + }, + { + "name": "Destination", + "value": f"{cidr_address}" + }, + { + "name": "SecurityGroupId", + "value": f"{lookup_sg_id}" + }, + { + "name": "Description", + "value": f"{lookup_sg_description}" + }, + { + "name": "Rule Description", + "value": f"{description}" + } + + ] + } + ] + } + + response = requests.post(TEAM_WEBHOOK, data=json.dumps(body)) + print(f'response from Teams: {response}') + + return response + + +def lambda_handler(event, context): + get_security_group(event)