Created
November 26, 2019 21:51
-
-
Save callum-p/031029a38c1069e6d4c42c42be503111 to your computer and use it in GitHub Desktop.
Revisions
-
callum-p created this gist
Nov 26, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,284 @@ Description: Creates alerts for root logins Parameters: emailRecipients: Type: CommaDelimitedList Default: '[email protected],[email protected]' slackChannel: Type: String Default: '#security-task-force' slackToken: Type: String NoEcho: true Mappings: accounts: '542423016982': account: dev '657252080847': account: prod '999262362450': account: uat '914027852804': account: sharedservices '715767712827': account: experimental '608847787792': account: backups '557740671058': account: users '989529009797': account: org root '556013944167': account: corporate Resources: snsTopic: Type: AWS::SNS::Topic Properties: DisplayName: Root Login Email Alerts lambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - sns:ListSubscriptionsByTopic - sns:Subscribe Resource: !Ref snsTopic rootLoginRule: Type: AWS::Events::Rule Properties: EventPattern: | { "detail-type": [ "AWS Console Sign In via CloudTrail" ], "detail": { "userIdentity": { "type": [ "Root" ] } } } State: ENABLED Targets: - Arn: !GetAtt slackNotifyFunction.Arn Id: slackNotify - Arn: !Ref snsTopic Id: emailNotify slackNotifyFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt lambdaExecutionRole.Arn Runtime: python3.6 Timeout: '300' Environment: Variables: SLACK_TOKEN: !Ref slackToken SLACK_CHANNEL: !Ref slackChannel ACCOUNT_ALIAS: Fn::FindInMap: - accounts - !Ref 'AWS::AccountId' - account Code: ZipFile: !Sub | #!/usr/bin/env python3 import urllib.request import os import json def handler(event, context): slackToken = os.getenv('SLACK_TOKEN') slackChannel = os.getenv('SLACK_CHANNEL') url = 'https://slack.com/api/chat.postMessage' account = os.getenv('ACCOUNT_ALIAS') headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {slackToken}' } values = { 'as_user': False, 'username': 'Root Login Alerts', 'channel': slackChannel, 'attachments': [{ 'color': 'danger', 'text': f'Root login for account {account} detected: ' + '```' + json.dumps(event, indent=4, sort_keys=True) + '```' }] } req = urllib.request.Request( url, data=json.dumps(values).encode('utf8'), headers=headers) with urllib.request.urlopen(req) as response: print(response.read()) print(response.getcode()) if __name__ == '__main__': event = { "version": "0", "id": "dd5d691d-a125-848c-ff9b-1debadd8c5fd", "detail-type": "AWS Console Sign In via CloudTrail", "source": "aws.signin", "account": "674647793584", "time": "2019-02-26T00:04:19Z", "region": "us-east-1", "resources": [], "detail": { "eventVersion": "1.05", "userIdentity": { "type": "IAMUser", "principalId": "xx", "arn": "arn:aws:iam::674647793584:user/xx.xx", "accountId": "674647793584", "userName": "xx.xx" }, "eventTime": "2019-02-26T00:04:19Z", "eventSource": "signin.amazonaws.com", "eventName": "ConsoleLogin", "awsRegion": "us-east-1", "sourceIPAddress": "xx.xx.xx.xx", "userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:65.0) " "Gecko/20100101 Firefox/65.0", "requestParameters": None, "responseElements": { "ConsoleLogin": "Success" }, "additionalEventData": { "LoginTo": "https://console.aws.amazon.com/console/home?" "state=hashArgs%23&isauthcode=true", "MobileVersion": "No", "MFAUsed": "No" }, "eventID": "98d1f04e-944e-47fb-a224-580089106881", "eventType": "AwsConsoleSignIn" } } handler(event, None) slackNotifyPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt rootLoginRule.Arn FunctionName: !Ref slackNotifyFunction snsTopicPolicy: Type: AWS::SNS::TopicPolicy Properties: PolicyDocument: Statement: - Effect: Allow Principal: Service: events.amazonaws.com Action: sns:Publish Resource: '*' Topics: - !Ref snsTopic susbcribeResource: Type: Custom::SnsSubscriber Properties: ServiceToken: !GetAtt subscribeFunction.Arn emailRecipients: !Ref emailRecipients topicArn: !Ref snsTopic subscribeFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.6 Timeout: '300' Handler: index.handler Role: !GetAtt lambdaExecutionRole.Arn Code: ZipFile: | #!/usr/bin/env python3 import boto3 import cfnresponse sns = boto3.client('sns') def create_subscription(topic_arn, email_address): sns.subscribe( TopicArn=topic_arn, Protocol='email', Endpoint=email_address) def get_subscriptions(topic_arn): subscriptions = [] args = {'TopicArn': topic_arn} while True: response = sns.list_subscriptions_by_topic(**args) for sub in response['Subscriptions']: if sub['Protocol'] == 'email': subscriptions.append(sub['Endpoint']) if 'NextToken' in response: args['NextToken'] = response['NextToken'] else: break return subscriptions def handler(event, context): if event['RequestType'] in ['Create', 'Update']: topic_arn = event['ResourceProperties']['topicArn'] subs = get_subscriptions(topic_arn) for recipient in event['ResourceProperties']['emailRecipients']: if recipient not in subs: create_subscription(topic_arn, recipient) if 'offline' not in event['ResourceProperties']: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) if __name__ == '__main__': event = { 'RequestType': 'Create', 'ResourceProperties': { 'emailRecipients': [ '[email protected]' ], 'topicArn': 'arn:aws:sns:ap-southeast-2:674647793584:' 'test-snsTopic-V7OB2POYPQOW', 'offline': True } } handler(event, None)