mobilefish

Untitled

Nov 16th, 2019
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 44.27 KB | None | 0 0
  1. AWSTemplateFormatVersion: 2010-09-09
  2. Description: S3 VirusScan
  3. Metadata:
  4. 'AWS::CloudFormation::Interface':
  5. ParameterGroups:
  6. - Label:
  7. default: Integrations
  8. Parameters:
  9. - SecurityHubIntegration
  10. - OpsCenterIntegration
  11. - CloudWatchIntegration
  12. - Label:
  13. default: Scan Parameters
  14. Parameters:
  15. - DeleteInfectedFiles
  16. - TagFiles
  17. - TagKey
  18. - ReportCleanFiles
  19. - Label:
  20. default: Auto Scaling Group Parameters
  21. Parameters:
  22. - AutoScalingMinSize
  23. - AutoScalingMaxSize
  24. - Label:
  25. default: EC2 Parameters
  26. Parameters:
  27. - KeyName
  28. - SSHIngressCidrIp
  29. - InstanceType
  30. - LogsRetentionInDays
  31. - VolumeSize
  32. - Label:
  33. default: Permissions Parameters
  34. Parameters:
  35. - S3BucketRestriction
  36. - S3ObjectRestriction
  37. Parameters:
  38. SecurityHubIntegration:
  39. Description: >-
  40. Forward infected files and scan failures to AWS Security Hub (Security Hub
  41. needs to be enabled in the region).
  42. Type: String
  43. Default: 'false'
  44. AllowedValues:
  45. - 'true'
  46. - 'false'
  47. OpsCenterIntegration:
  48. Description: Forward infected files and scan failures to AWS Systems Manager OpsCenter.
  49. Type: String
  50. Default: 'false'
  51. AllowedValues:
  52. - 'true'
  53. - 'false'
  54. CloudWatchIntegration:
  55. Description: Generate CloudWatch Metrics with scan results.
  56. Type: String
  57. Default: 'true'
  58. AllowedValues:
  59. - 'true'
  60. - 'false'
  61. DeleteInfectedFiles:
  62. Description: Automatically delete infected files.
  63. Type: String
  64. Default: 'true'
  65. AllowedValues:
  66. - 'true'
  67. - 'false'
  68. ReportCleanFiles:
  69. Description: Report clean files to the SNS topic.
  70. Type: String
  71. Default: 'true'
  72. AllowedValues:
  73. - 'true'
  74. - 'false'
  75. TagFiles:
  76. Description: >-
  77. Tag S3 object upon successful scan accordingly with values of "clean",
  78. "infected", or "no" (infected only works if DeleteInfectedFiles != true)
  79. using the tag key specified by TagKey.
  80. Type: String
  81. Default: 'true'
  82. AllowedValues:
  83. - 'true'
  84. - 'false'
  85. TagKey:
  86. Description: 'S3 object tag key used to specify values of "clean", "infected", or "no".'
  87. Type: String
  88. Default: s3-virusscan
  89. AutoScalingMinSize:
  90. Description: Minimum number of EC2 instances scanning files.
  91. Type: Number
  92. Default: 1
  93. ConstraintDescription: Must be >= 1
  94. MinValue: 1
  95. AutoScalingMaxSize:
  96. Description: Maximum number of EC2 instances scanning files.
  97. Type: Number
  98. Default: 1
  99. ConstraintDescription: Must be >= 1
  100. MinValue: 1
  101. KeyName:
  102. Description: 'Name of the EC2 key pair to log in via SSH (username: ec2-user).'
  103. Type: 'AWS::EC2::KeyPair::KeyName'
  104. InstanceType:
  105. Description: Specifies the instance type of the EC2 instance
  106. Type: String
  107. Default: m5.large
  108. AllowedValues:
  109. - t3a.small
  110. - t3a.medium
  111. - t3a.large
  112. - t3a.xlarge
  113. - t3a.2xlarge
  114. - t3.small
  115. - t3.medium
  116. - t3.large
  117. - t3.xlarge
  118. - t3.2xlarge
  119. - m5a.large
  120. - m5a.xlarge
  121. - m5a.2xlarge
  122. - m5a.4xlarge
  123. - m5a.8xlarge
  124. - m5a.12xlarge
  125. - m5a.16xlarge
  126. - m5a.24xlarge
  127. - m5.large
  128. - m5.xlarge
  129. - m5.2xlarge
  130. - m5.4xlarge
  131. - m5.8xlarge
  132. - m5.12xlarge
  133. - m5.16xlarge
  134. - m5.24xlarge
  135. - m4.large
  136. - m4.xlarge
  137. - m4.2xlarge
  138. - m4.4xlarge
  139. - m4.10xlarge
  140. - m4.16xlarge
  141. LogsRetentionInDays:
  142. Description: Specifies the number of days you want to retain log events.
  143. Type: Number
  144. Default: 14
  145. AllowedValues:
  146. - 1
  147. - 3
  148. - 5
  149. - 7
  150. - 14
  151. - 30
  152. - 60
  153. - 90
  154. - 120
  155. - 150
  156. - 180
  157. - 365
  158. - 400
  159. - 545
  160. - 731
  161. - 1827
  162. - 3653
  163. VolumeSize:
  164. Description: >-
  165. The size of the volume, in gibibytes (GiBs). You can only scan files that
  166. are smaller than helf of this value.
  167. Type: Number
  168. Default: 8
  169. ConstraintDescription: 'Must be in the range [8-1024]'
  170. MinValue: 8
  171. MaxValue: 1024
  172. SSHIngressCidrIp:
  173. Description: >-
  174. Optional ingress rule allows SSH access from this IP address range (e.g.,
  175. access from anywhere: 0.0.0.0/0, from single IP address 91.45.138.21/32)
  176. Type: String
  177. Default: ''
  178. S3BucketRestriction:
  179. Description: >-
  180. Restrict access to specific S3 buckets (e.g.
  181. arn:aws:s3:::s3-virusscan-a,arn:aws:s3:::s3-virusscan-b or * to allow
  182. access to all S3 buckets).
  183. Type: CommaDelimitedList
  184. Default: '*'
  185. S3ObjectRestriction:
  186. Description: >-
  187. Restrict access to specific S3 objects (e.g.
  188. arn:aws:s3:::s3-virusscan-a/*,arn:aws:s3:::s3-virusscan-b/* or * to allow
  189. access to all S3 objects).
  190. Type: CommaDelimitedList
  191. Default: '*'
  192. Conditions:
  193. HasSecurityHubIntegration: !<!Equals>
  194. - !<!Ref> SecurityHubIntegration
  195. - 'true'
  196. HasOpsCenterIntegration: !<!Equals>
  197. - !<!Ref> OpsCenterIntegration
  198. - 'true'
  199. HasCloudWatchIntegration: !<!Equals>
  200. - !<!Ref> CloudWatchIntegration
  201. - 'true'
  202. HasDeleteInfectedFiles: !<!Equals>
  203. - !<!Ref> DeleteInfectedFiles
  204. - 'true'
  205. HasTagFiles: !<!Equals>
  206. - !<!Ref> TagFiles
  207. - 'true'
  208. HasSSHIngressCidrIp: !<!Not>
  209. - !<!Equals>
  210. - !<!Ref> SSHIngressCidrIp
  211. - ''
  212. Mappings:
  213. RegionMap:
  214. us-east-1:
  215. AMI: ami-090c254a59e5700e7
  216. us-east-2:
  217. AMI: ami-0d5b903b4f3cd7118
  218. us-west-1:
  219. AMI: ami-0256db1fa2dd0d0d8
  220. us-west-2:
  221. AMI: ami-083bca78f3e03c51e
  222. eu-west-1:
  223. AMI: ami-0a5f5ac4b648932e3
  224. eu-west-2:
  225. AMI: ami-0540b01de123f44bf
  226. eu-west-3:
  227. AMI: ami-095dffd5b3ff2a971
  228. eu-central-1:
  229. AMI: ami-0d49abfd983e4c175
  230. ca-central-1:
  231. AMI: ami-0497ff4107c2c00a0
  232. ap-southeast-1:
  233. AMI: ami-0d597b1ce1659b16b
  234. ap-northeast-1:
  235. AMI: ami-0f4b3d8ba2212daff
  236. ap-northeast-2:
  237. AMI: ami-0b073992f76339934
  238. sa-east-1:
  239. AMI: ami-043ae1117d1d3229e
  240. ap-southeast-2:
  241. AMI: ami-0ed4704ab99d8c6cd
  242. ap-south-1:
  243. AMI: ami-050d9f3486d9a18fb
  244. eu-north-1:
  245. AMI: ami-b21c97cc
  246. Resources:
  247. VPC:
  248. Type: 'AWS::EC2::VPC'
  249. Properties:
  250. CidrBlock: 10.0.0.0/16
  251. EnableDnsSupport: true
  252. EnableDnsHostnames: true
  253. InstanceTenancy: default
  254. Tags:
  255. - Key: Name
  256. Value: !<!Ref> 'AWS::StackName'
  257. InternetGateway:
  258. Type: 'AWS::EC2::InternetGateway'
  259. Properties:
  260. Tags:
  261. - Key: Name
  262. Value: !<!Ref> 'AWS::StackName'
  263. VPCGatewayAttachment:
  264. Type: 'AWS::EC2::VPCGatewayAttachment'
  265. Properties:
  266. VpcId: !<!Ref> VPC
  267. InternetGatewayId: !<!Ref> InternetGateway
  268. SubnetAPublic:
  269. Type: 'AWS::EC2::Subnet'
  270. Properties:
  271. AvailabilityZone: !<!Select>
  272. - 0
  273. - !<!GetAZs> ''
  274. CidrBlock: 10.0.0.0/20
  275. MapPublicIpOnLaunch: true
  276. VpcId: !<!Ref> VPC
  277. Tags:
  278. - Key: Name
  279. Value: !<!Sub> '${AWS::StackName}: A public'
  280. SubnetBPublic:
  281. Type: 'AWS::EC2::Subnet'
  282. Properties:
  283. AvailabilityZone: !<!Select>
  284. - 1
  285. - !<!GetAZs> ''
  286. CidrBlock: 10.0.32.0/20
  287. MapPublicIpOnLaunch: true
  288. VpcId: !<!Ref> VPC
  289. Tags:
  290. - Key: Name
  291. Value: !<!Sub> '${AWS::StackName}: B public'
  292. RouteTableAPublic:
  293. Type: 'AWS::EC2::RouteTable'
  294. Properties:
  295. VpcId: !<!Ref> VPC
  296. Tags:
  297. - Key: Name
  298. Value: !<!Sub> '${AWS::StackName}: A Public'
  299. RouteTableBPublic:
  300. Type: 'AWS::EC2::RouteTable'
  301. Properties:
  302. VpcId: !<!Ref> VPC
  303. Tags:
  304. - Key: Name
  305. Value: !<!Sub> '${AWS::StackName}: B Public'
  306. RouteTableAssociationAPublic:
  307. Type: 'AWS::EC2::SubnetRouteTableAssociation'
  308. Properties:
  309. SubnetId: !<!Ref> SubnetAPublic
  310. RouteTableId: !<!Ref> RouteTableAPublic
  311. RouteTableAssociationBPublic:
  312. Type: 'AWS::EC2::SubnetRouteTableAssociation'
  313. Properties:
  314. SubnetId: !<!Ref> SubnetBPublic
  315. RouteTableId: !<!Ref> RouteTableBPublic
  316. RouteTableAPublicInternetRoute:
  317. Type: 'AWS::EC2::Route'
  318. DependsOn: VPCGatewayAttachment
  319. Properties:
  320. RouteTableId: !<!Ref> RouteTableAPublic
  321. DestinationCidrBlock: 0.0.0.0/0
  322. GatewayId: !<!Ref> InternetGateway
  323. RouteTableBPublicInternetRoute:
  324. Type: 'AWS::EC2::Route'
  325. DependsOn: VPCGatewayAttachment
  326. Properties:
  327. RouteTableId: !<!Ref> RouteTableBPublic
  328. DestinationCidrBlock: 0.0.0.0/0
  329. GatewayId: !<!Ref> InternetGateway
  330. NetworkAclPublic:
  331. Type: 'AWS::EC2::NetworkAcl'
  332. Properties:
  333. VpcId: !<!Ref> VPC
  334. Tags:
  335. - Key: Name
  336. Value: !<!Sub> '${AWS::StackName}: Public'
  337. SubnetNetworkAclAssociationAPublic:
  338. Type: 'AWS::EC2::SubnetNetworkAclAssociation'
  339. Properties:
  340. SubnetId: !<!Ref> SubnetAPublic
  341. NetworkAclId: !<!Ref> NetworkAclPublic
  342. SubnetNetworkAclAssociationBPublic:
  343. Type: 'AWS::EC2::SubnetNetworkAclAssociation'
  344. Properties:
  345. SubnetId: !<!Ref> SubnetBPublic
  346. NetworkAclId: !<!Ref> NetworkAclPublic
  347. NetworkAclEntryInPublicAllowAll:
  348. Type: 'AWS::EC2::NetworkAclEntry'
  349. Properties:
  350. NetworkAclId: !<!Ref> NetworkAclPublic
  351. RuleNumber: 99
  352. Protocol: -1
  353. RuleAction: allow
  354. Egress: false
  355. CidrBlock: 0.0.0.0/0
  356. NetworkAclEntryOutPublicAllowAll:
  357. Type: 'AWS::EC2::NetworkAclEntry'
  358. Properties:
  359. NetworkAclId: !<!Ref> NetworkAclPublic
  360. RuleNumber: 99
  361. Protocol: -1
  362. RuleAction: allow
  363. Egress: true
  364. CidrBlock: 0.0.0.0/0
  365. FindingsTopic:
  366. Type: 'AWS::SNS::Topic'
  367. Properties: {}
  368. ScanQueue:
  369. Type: 'AWS::SQS::Queue'
  370. Properties:
  371. VisibilityTimeout: 300
  372. RedrivePolicy:
  373. deadLetterTargetArn: !<!GetAtt> DeadLetterQueue.Arn
  374. maxReceiveCount: 3
  375. ScanQueueFullAlarm:
  376. Type: 'AWS::CloudWatch::Alarm'
  377. Properties:
  378. AlarmActions:
  379. - !<!Ref> ScanScaleUp
  380. ComparisonOperator: GreaterThanThreshold
  381. Dimensions:
  382. - Name: QueueName
  383. Value: !<!GetAtt> ScanQueue.QueueName
  384. EvaluationPeriods: 1
  385. MetricName: ApproximateNumberOfMessagesVisible
  386. Namespace: AWS/SQS
  387. Period: 300
  388. Statistic: Maximum
  389. Threshold: 0
  390. TreatMissingData: notBreaching
  391. ScanQueueEmptyAlarm:
  392. Type: 'AWS::CloudWatch::Alarm'
  393. Properties:
  394. AlarmActions:
  395. - !<!Ref> ScanScaleDown
  396. ComparisonOperator: LessThanOrEqualToThreshold
  397. Dimensions:
  398. - Name: QueueName
  399. Value: !<!GetAtt> ScanQueue.QueueName
  400. EvaluationPeriods: 1
  401. MetricName: ApproximateNumberOfMessagesVisible
  402. Namespace: AWS/SQS
  403. Period: 300
  404. Statistic: Maximum
  405. Threshold: 0
  406. TreatMissingData: notBreaching
  407. ScanQueuePolicy:
  408. Type: 'AWS::SQS::QueuePolicy'
  409. Properties:
  410. PolicyDocument:
  411. Version: 2012-10-17
  412. Statement:
  413. - Effect: Allow
  414. Principal:
  415. AWS: '*'
  416. Action: 'SQS:SendMessage'
  417. Resource: !<!GetAtt> ScanQueue.Arn
  418. Queues:
  419. - !<!Ref> ScanQueue
  420. DeadLetterQueue:
  421. Type: 'AWS::SQS::Queue'
  422. Properties:
  423. MessageRetentionPeriod: 1209600
  424. DeadLetterQueueAlarm:
  425. Type: 'AWS::CloudWatch::Alarm'
  426. Properties:
  427. AlarmDescription: Alarm if dead letter queue has messages
  428. Namespace: AWS/SQS
  429. MetricName: ApproximateNumberOfMessagesVisible
  430. Dimensions:
  431. - Name: QueueName
  432. Value: !<!GetAtt> DeadLetterQueue.QueueName
  433. Statistic: Sum
  434. Period: 60
  435. EvaluationPeriods: 1
  436. Threshold: 1
  437. ComparisonOperator: GreaterThanOrEqualToThreshold
  438. AlarmActions:
  439. - !<!Ref> FindingsTopic
  440. TreatMissingData: notBreaching
  441. ScanQueueOldMessagesAlarm:
  442. Type: 'AWS::CloudWatch::Alarm'
  443. Properties:
  444. AlarmDescription: Alarm if scan queue contains a message older than 1 hour
  445. Namespace: AWS/SQS
  446. MetricName: ApproximateAgeOfOldestMessage
  447. Dimensions:
  448. - Name: QueueName
  449. Value: !<!GetAtt> ScanQueue.QueueName
  450. Statistic: Maximum
  451. Period: 60
  452. EvaluationPeriods: 1
  453. Threshold: 3600
  454. ComparisonOperator: GreaterThanOrEqualToThreshold
  455. AlarmActions:
  456. - !<!Ref> FindingsTopic
  457. TreatMissingData: notBreaching
  458. ScanAutoScalingGroup:
  459. DependsOn: VPCGatewayAttachment
  460. Type: 'AWS::AutoScaling::AutoScalingGroup'
  461. Properties:
  462. LaunchConfigurationName: !<!Ref> ScanLaunchConfiguration
  463. MetricsCollection:
  464. - Granularity: 1Minute
  465. Metrics:
  466. - GroupTotalInstances
  467. - GroupMaxSize
  468. MaxSize: !<!Ref> AutoScalingMaxSize
  469. MinSize: !<!Ref> AutoScalingMinSize
  470. VPCZoneIdentifier:
  471. - !<!Ref> SubnetAPublic
  472. - !<!Ref> SubnetBPublic
  473. Tags:
  474. - Key: Name
  475. Value: !<!Ref> 'AWS::StackName'
  476. PropagateAtLaunch: true
  477. CreationPolicy:
  478. ResourceSignal:
  479. Timeout: PT10M
  480. UpdatePolicy:
  481. AutoScalingRollingUpdate:
  482. PauseTime: PT10M
  483. WaitOnResourceSignals: true
  484. ScanScaleUp:
  485. Type: 'AWS::AutoScaling::ScalingPolicy'
  486. Properties:
  487. AdjustmentType: ChangeInCapacity
  488. AutoScalingGroupName: !<!Ref> ScanAutoScalingGroup
  489. EstimatedInstanceWarmup: 300
  490. MetricAggregationType: Maximum
  491. PolicyType: StepScaling
  492. StepAdjustments:
  493. - MetricIntervalLowerBound: 0
  494. MetricIntervalUpperBound: 25
  495. ScalingAdjustment: 1
  496. - MetricIntervalLowerBound: 25
  497. MetricIntervalUpperBound: 100
  498. ScalingAdjustment: 2
  499. - MetricIntervalLowerBound: 100
  500. MetricIntervalUpperBound: 400
  501. ScalingAdjustment: 4
  502. - MetricIntervalLowerBound: 400
  503. MetricIntervalUpperBound: 1600
  504. ScalingAdjustment: 8
  505. - MetricIntervalLowerBound: 1600
  506. MetricIntervalUpperBound: 6400
  507. ScalingAdjustment: 16
  508. - MetricIntervalLowerBound: 6400
  509. MetricIntervalUpperBound: 25600
  510. ScalingAdjustment: 32
  511. - MetricIntervalLowerBound: 25600
  512. ScalingAdjustment: 64
  513. ScanScaleDown:
  514. Type: 'AWS::AutoScaling::ScalingPolicy'
  515. Properties:
  516. AdjustmentType: PercentChangeInCapacity
  517. AutoScalingGroupName: !<!Ref> ScanAutoScalingGroup
  518. Cooldown: '300'
  519. MinAdjustmentMagnitude: 1
  520. PolicyType: SimpleScaling
  521. ScalingAdjustment: -25
  522. ScanInstanceProfile:
  523. Type: 'AWS::IAM::InstanceProfile'
  524. Properties:
  525. Roles:
  526. - !<!Ref> ScanIAMRole
  527. ScanIAMRole:
  528. Type: 'AWS::IAM::Role'
  529. Properties:
  530. AssumeRolePolicyDocument:
  531. Version: 2012-10-17
  532. Statement:
  533. - Effect: Allow
  534. Principal:
  535. Service: ec2.amazonaws.com
  536. Action: 'sts:AssumeRole'
  537. Policies:
  538. - PolicyName: s3read
  539. PolicyDocument:
  540. Version: 2012-10-17
  541. Statement:
  542. - Effect: Allow
  543. Action:
  544. - 's3:GetObject*'
  545. Resource: !<!Ref> S3ObjectRestriction
  546. - Effect: Allow
  547. Action:
  548. - 's3:ListBucket*'
  549. Resource: !<!Ref> S3BucketRestriction
  550. - PolicyName: sqs
  551. PolicyDocument:
  552. Version: 2012-10-17
  553. Statement:
  554. - Effect: Allow
  555. Action:
  556. - 'sqs:DeleteMessage'
  557. - 'sqs:ReceiveMessage'
  558. Resource: !<!GetAtt> ScanQueue.Arn
  559. - PolicyName: sns
  560. PolicyDocument:
  561. Version: 2012-10-17
  562. Statement:
  563. - Effect: Allow
  564. Action: 'sns:Publish'
  565. Resource: !<!Ref> FindingsTopic
  566. - PolicyName: logs
  567. PolicyDocument:
  568. Version: 2012-10-17
  569. Statement:
  570. - Effect: Allow
  571. Action:
  572. - 'logs:CreateLogGroup'
  573. - 'logs:CreateLogStream'
  574. - 'logs:PutLogEvents'
  575. - 'logs:DescribeLogStreams'
  576. Resource: 'arn:aws:logs:*:*:*'
  577. DeletePolicy:
  578. Type: 'AWS::IAM::Policy'
  579. Condition: HasDeleteInfectedFiles
  580. Properties:
  581. PolicyDocument:
  582. Version: 2012-10-17
  583. Statement:
  584. - Effect: Allow
  585. Action:
  586. - 's3:DeleteObject*'
  587. Resource: !<!Ref> S3ObjectRestriction
  588. PolicyName: s3delete
  589. Roles:
  590. - !<!Ref> ScanIAMRole
  591. TagObjectPolicy:
  592. Type: 'AWS::IAM::Policy'
  593. Condition: HasTagFiles
  594. Properties:
  595. PolicyDocument:
  596. Version: 2012-10-17
  597. Statement:
  598. - Effect: Allow
  599. Action:
  600. - 's3:PutObjectTagging'
  601. - 's3:PutObjectVersionTagging'
  602. Resource: !<!Ref> S3ObjectRestriction
  603. Condition:
  604. 'ForAllValues:StringLike':
  605. 's3:RequestObjectTagKeys': !<!Ref> TagKey
  606. PolicyName: s3objecttag
  607. Roles:
  608. - !<!Ref> ScanIAMRole
  609. Logs:
  610. Type: 'AWS::Logs::LogGroup'
  611. Properties:
  612. RetentionInDays: !<!Ref> LogsRetentionInDays
  613. ScanLaunchConfiguration:
  614. Type: 'AWS::AutoScaling::LaunchConfiguration'
  615. Metadata:
  616. 'AWS::CloudFormation::Init':
  617. config:
  618. files:
  619. /etc/awslogs/awscli.conf:
  620. content: !<!Sub> |
  621. [default]
  622. region = ${AWS::Region}
  623. [plugins]
  624. cwlogs = cwlogs
  625. mode: '000644'
  626. owner: root
  627. group: root
  628. /etc/awslogs/config/files.conf:
  629. content: !<!Sub> |
  630. [/var/log/audit/audit.log]
  631. file = /var/log/audit/audit.log
  632. log_stream_name = {instance_id}/var/log/audit/audit.log
  633. log_group_name = ${Logs}
  634. [/var/log/awslogs.log]
  635. datetime_format = %Y-%m-%d %H:%M:%S
  636. file = /var/log/awslogs.log
  637. log_stream_name = {instance_id}/var/log/awslogs.log
  638. log_group_name = ${Logs}
  639. [/var/log/boot.log]
  640. file = /var/log/boot.log
  641. log_stream_name = {instance_id}/var/log/boot.log
  642. log_group_name = ${Logs}
  643. [/var/log/cfn-hup.log]
  644. datetime_format = %Y-%m-%d %H:%M:%S
  645. file = /var/log/cfn-hup.log
  646. log_stream_name = {instance_id}/var/log/cfn-hup.log
  647. log_group_name = ${Logs}
  648. [/var/log/cfn-init-cmd.log]
  649. datetime_format = %Y-%m-%d %H:%M:%S
  650. file = /var/log/cfn-init-cmd.log
  651. log_stream_name = {instance_id}/var/log/cfn-init-cmd.log
  652. log_group_name = ${Logs}
  653. [/var/log/cfn-init.log]
  654. datetime_format = %Y-%m-%d %H:%M:%S
  655. file = /var/log/cfn-init.log
  656. log_stream_name = {instance_id}/var/log/cfn-init.log
  657. log_group_name = ${Logs}
  658. [/var/log/cfn-wire.log]
  659. datetime_format = %Y-%m-%d %H:%M:%S
  660. file = /var/log/cfn-wire.log
  661. log_stream_name = {instance_id}/var/log/cfn-wire.log
  662. log_group_name = ${Logs}
  663. [/var/log/cloud-init-output.log]
  664. file = /var/log/cloud-init-output.log
  665. log_stream_name = {instance_id}/var/log/cloud-init-output.log
  666. log_group_name = ${Logs}
  667. [/var/log/cloud-init.log]
  668. datetime_format = %b %d %H:%M:%S
  669. file = /var/log/cloud-init.log
  670. log_stream_name = {instance_id}/var/log/cloud-init.log
  671. log_group_name = ${Logs}
  672. [/var/log/cron]
  673. datetime_format = %b %d %H:%M:%S
  674. file = /var/log/cron
  675. log_stream_name = {instance_id}/var/log/cron
  676. log_group_name = ${Logs}
  677. [/var/log/dmesg]
  678. file = /var/log/dmesg
  679. log_stream_name = {instance_id}/var/log/dmesg
  680. log_group_name = ${Logs}
  681. [/var/log/freshclam.log]
  682. file = /var/log/freshclam.log
  683. log_stream_name = {instance_id}/var/log/freshclam.log
  684. log_group_name = ${Logs}
  685. [/var/log/grubby_prune_debug]
  686. file = /var/log/grubby_prune_debug
  687. log_stream_name = {instance_id}/var/log/grubby_prune_debug
  688. log_group_name = ${Logs}
  689. [/var/log/maillog]
  690. datetime_format = %b %d %H:%M:%S
  691. file = /var/log/maillog
  692. log_stream_name = {instance_id}/var/log/maillog
  693. log_group_name = ${Logs}
  694. [/var/log/messages]
  695. datetime_format = %b %d %H:%M:%S
  696. file = /var/log/messages
  697. log_stream_name = {instance_id}/var/log/messages
  698. log_group_name = ${Logs}
  699. [/var/log/secure]
  700. datetime_format = %b %d %H:%M:%S
  701. file = /var/log/secure
  702. log_stream_name = {instance_id}/var/log/secure
  703. log_group_name = ${Logs}
  704. [/var/log/yum.log]
  705. datetime_format = %b %d %H:%M:%S
  706. file = /var/log/yum.log
  707. log_stream_name = {instance_id}/var/log/yum.log
  708. log_group_name = ${Logs}
  709. mode: '000644'
  710. owner: root
  711. group: root
  712. /opt/s3-virusscan/s3-virusscan.conf:
  713. content: !<!Sub> |
  714. delete: ${DeleteInfectedFiles}
  715. report_clean: ${ReportCleanFiles}
  716. tag_files: ${TagFiles}
  717. tag_key: ${TagKey}
  718. region: ${AWS::Region}
  719. queue: ${ScanQueue}
  720. topic: ${FindingsTopic}
  721. volume_size: ${VolumeSize}
  722. mode: '000644'
  723. owner: ec2-user
  724. group: ec2-user
  725. /etc/cfn/cfn-hup.conf:
  726. content: !<!Sub> |
  727. [main]
  728. stack=${AWS::StackId}
  729. region=${AWS::Region}
  730. interval=1
  731. mode: '000400'
  732. owner: root
  733. group: root
  734. /etc/cfn/hooks.d/cfn-auto-reloader.conf:
  735. content: !<!Sub> >
  736. [cfn-auto-reloader-hook]
  737.  
  738. triggers=post.update
  739.  
  740. path=Resources.ScanLaunchConfiguration.Metadata.AWS::CloudFormation::Init
  741.  
  742. action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName}
  743. --resource ScanLaunchConfiguration --region ${AWS::Region}
  744.  
  745. runas=root
  746. services:
  747. sysvinit:
  748. awslogsd:
  749. enabled: true
  750. ensureRunning: true
  751. files:
  752. - /etc/awslogs/awscli.conf
  753. - /etc/awslogs/config/files.conf
  754. cfn-hup:
  755. enabled: true
  756. ensureRunning: true
  757. files:
  758. - /etc/cfn/cfn-hup.conf
  759. - /etc/cfn/hooks.d/cfn-auto-reloader.conf
  760. s3-virusscan:
  761. enabled: true
  762. ensureRunning: true
  763. files:
  764. - /opt/s3-virusscan/s3-virusscan.conf
  765. Properties:
  766. KeyName: !<!Ref> KeyName
  767. AssociatePublicIpAddress: true
  768. EbsOptimized: false
  769. BlockDeviceMappings:
  770. - DeviceName: /dev/xvda
  771. Ebs:
  772. VolumeSize: !<!Ref> VolumeSize
  773. VolumeType: gp2
  774. IamInstanceProfile: !<!Ref> ScanInstanceProfile
  775. ImageId: !<!FindInMap>
  776. - RegionMap
  777. - !<!Ref> 'AWS::Region'
  778. - AMI
  779. InstanceType: !<!Ref> InstanceType
  780. SecurityGroups:
  781. - !<!Ref> ScanSecurityGroup
  782. UserData: !<!Base64>
  783. 'Fn::Sub': >
  784. #!/bin/bash -ex
  785.  
  786. trap '/opt/aws/bin/cfn-signal -e 1 --stack ${AWS::StackName}
  787. --resource ScanAutoScalingGroup --region ${AWS::Region}' ERR
  788.  
  789. /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource
  790. ScanLaunchConfiguration --region ${AWS::Region}
  791.  
  792. /opt/aws/bin/cfn-signal -e 0 --stack ${AWS::StackName} --resource
  793. ScanAutoScalingGroup --region ${AWS::Region}
  794. ScanSecurityGroup:
  795. Type: 'AWS::EC2::SecurityGroup'
  796. Properties:
  797. GroupDescription: !<!Ref> 'AWS::StackName'
  798. VpcId: !<!Ref> VPC
  799. ScanSecurityGroupInSSH:
  800. Condition: HasSSHIngressCidrIp
  801. Type: 'AWS::EC2::SecurityGroupIngress'
  802. Properties:
  803. GroupId: !<!Ref> ScanSecurityGroup
  804. IpProtocol: tcp
  805. FromPort: 22
  806. ToPort: 22
  807. CidrIp: !<!Ref> SSHIngressCidrIp
  808. Dashboard:
  809. Type: 'AWS::CloudWatch::Dashboard'
  810. Properties:
  811. DashboardBody: !<!Sub> |
  812. {
  813. "widgets": [{
  814. "type": "metric",
  815. "x": 0,
  816. "y": 0,
  817. "width": 6,
  818. "height": 6,
  819. "properties": {
  820. "metrics": [
  821. [ "${AWS::StackName}", "clean", { "period": 300, "stat": "Sum", "label": "Clean" } ],
  822. [ ".", "infected", { "period": 300, "stat": "Sum", "color": "#d62728", "label": "Infected" } ],
  823. [ ".", "no", { "period": 300, "stat": "Sum", "color": "#ff7f0e", "label": "No" } ]
  824. ],
  825. "view": "timeSeries",
  826. "stacked": true,
  827. "region": "${AWS::Region}",
  828. "title": "Scan results (requires CloudWatch Integration)",
  829. "period": 300
  830. }
  831. }, {
  832. "type": "metric",
  833. "x": 6,
  834. "y": 0,
  835. "width": 6,
  836. "height": 6,
  837. "properties": {
  838. "metrics": [
  839. [ "AWS/SQS", "ApproximateNumberOfMessagesVisible", "QueueName", "${ScanQueue.QueueName}", { "label": "Length" } ]
  840. ],
  841. "view": "timeSeries",
  842. "stacked": false,
  843. "region": "${AWS::Region}",
  844. "title": "Scan Queue Length",
  845. "period": 300
  846. }
  847. }, {
  848. "type": "metric",
  849. "x": 12,
  850. "y": 0,
  851. "width": 6,
  852. "height": 6,
  853. "properties": {
  854. "metrics": [
  855. [ "AWS/AutoScaling", "GroupTotalInstances", "AutoScalingGroupName", "${ScanAutoScalingGroup}", { "stat": "Average", "label": "Running Instances" } ],
  856. [ ".", "GroupMaxSize", ".", ".", { "label": "Max Instances", "color": "#d62728" } ]
  857. ],
  858. "view": "timeSeries",
  859. "stacked": false,
  860. "region": "${AWS::Region}",
  861. "title": "Scan Fleet",
  862. "period": 300
  863. }
  864. }, {
  865. "type": "log",
  866. "x": 0,
  867. "y": 6,
  868. "width": 24,
  869. "height": 6,
  870. "properties": {
  871. "query": "SOURCE '${Logs}' | fields @timestamp, @message\n| filter @logStream like /messages/ and @message like /s3-virusscan/\n| sort @timestamp desc\n| limit 100",
  872. "region": "${AWS::Region}",
  873. "stacked": false,
  874. "title": "Scan Logs",
  875. "view": "table"
  876. }
  877. }, {
  878. "type": "log",
  879. "x": 0,
  880. "y": 12,
  881. "width": 24,
  882. "height": 6,
  883. "properties": {
  884. "query": "SOURCE '${Logs}' | fields @timestamp, @message\n| sort @timestamp desc\n| limit 100",
  885. "region": "${AWS::Region}",
  886. "stacked": false,
  887. "title": "System Logs",
  888. "view": "table"
  889. }
  890. }]
  891. }
  892. DashboardName: !<!Ref> 'AWS::StackName'
  893. SecurityHubIntegrationSubscription:
  894. Condition: HasSecurityHubIntegration
  895. DependsOn:
  896. - SecurityHubIntegrationLambdaPermission
  897. - SecurityHubIntegrationLambdaPolicy
  898. Type: 'AWS::SNS::Subscription'
  899. Properties:
  900. Endpoint: !<!GetAtt> SecurityHubIntegrationLambdaFunction.Arn
  901. FilterPolicy:
  902. status:
  903. - infected
  904. - 'no'
  905. Protocol: lambda
  906. TopicArn: !<!Ref> FindingsTopic
  907. SecurityHubIntegrationLambdaPermission:
  908. Condition: HasSecurityHubIntegration
  909. Type: 'AWS::Lambda::Permission'
  910. Properties:
  911. Action: 'lambda:InvokeFunction'
  912. FunctionName: !<!Ref> SecurityHubIntegrationLambdaFunction
  913. Principal: sns.amazonaws.com
  914. SourceArn: !<!Ref> FindingsTopic
  915. SecurityHubIntegrationLambdaRole:
  916. Condition: HasSecurityHubIntegration
  917. Type: 'AWS::IAM::Role'
  918. Properties:
  919. AssumeRolePolicyDocument:
  920. Version: 2012-10-17
  921. Statement:
  922. - Effect: Allow
  923. Principal:
  924. Service: lambda.amazonaws.com
  925. Action: 'sts:AssumeRole'
  926. Policies:
  927. - PolicyName: securityhub
  928. PolicyDocument:
  929. Statement:
  930. - Effect: Allow
  931. Action: 'securityhub:BatchImportFindings'
  932. Resource: '*'
  933. Condition:
  934. StringEquals:
  935. 'securityhub:TargetAccount': !<!Ref> 'AWS::AccountId'
  936. SecurityHubIntegrationLambdaPolicy:
  937. Condition: HasSecurityHubIntegration
  938. Type: 'AWS::IAM::Policy'
  939. Properties:
  940. Roles:
  941. - !<!Ref> SecurityHubIntegrationLambdaRole
  942. PolicyName: logs
  943. PolicyDocument:
  944. Statement:
  945. - Effect: Allow
  946. Action:
  947. - 'logs:CreateLogStream'
  948. - 'logs:PutLogEvents'
  949. Resource: !<!GetAtt> SecurityHubIntegrationLambdaLogGroup.Arn
  950. SecurityHubIntegrationLambdaFunction:
  951. Condition: HasSecurityHubIntegration
  952. Type: 'AWS::Lambda::Function'
  953. Properties:
  954. Code:
  955. ZipFile: !<!Sub> |
  956. 'use strict';
  957. const AWS = require('aws-sdk');
  958. const securityhub = new AWS.SecurityHub({apiVersion: '2018-10-26'});
  959. function generateTitle(record) {
  960. if (record.Sns.MessageAttributes.status.Value === 'no') {
  961. return 'File scan failed';
  962. } else if (record.Sns.MessageAttributes.status.Value === 'infected') {
  963. return 'Infected file found';
  964. } else {
  965. throw new Error(`unsupported status: ${!record.Sns.MessageAttributes.status.Value}`);
  966. }
  967. }
  968. function generateDescription(record) {
  969. if (record.Sns.MessageAttributes.status.Value === 'no') {
  970. if ('version' in record.Sns.MessageAttributes) {
  971. return `File in S3 bucket ${!record.Sns.MessageAttributes.bucket.Value} with key ${!record.Sns.MessageAttributes.key.Value} and version ${!record.Sns.MessageAttributes.version.Value} could not be scanned, ${!record.Sns.MessageAttributes.action.Value} action executed`;
  972. } else {
  973. return `File in S3 bucket ${!record.Sns.MessageAttributes.bucket.Value} with key ${!record.Sns.MessageAttributes.key.Value} could not be scanned, ${!record.Sns.MessageAttributes.action.Value} action executed`;
  974. }
  975. } else if (record.Sns.MessageAttributes.status.Value === 'infected') {
  976. if ('version' in record.Sns.MessageAttributes) {
  977. return `Infected file found in S3 bucket ${!record.Sns.MessageAttributes.bucket.Value} with key ${!record.Sns.MessageAttributes.key.Value} and version ${!record.Sns.MessageAttributes.version.Value}, ${!record.Sns.MessageAttributes.action.Value} action executed`;
  978. } else {
  979. return `Infected file found in S3 bucket ${!record.Sns.MessageAttributes.bucket.Value} with key ${!record.Sns.MessageAttributes.key.Value}, ${!record.Sns.MessageAttributes.action.Value} action executed`;
  980. }
  981. } else {
  982. throw new Error(`unsupported status: ${!record.Sns.MessageAttributes.status.Value}`);
  983. }
  984. }
  985. exports.handler = async (event) => {
  986. console.log(`Invoke: ${!JSON.stringify(event)}`);
  987. try {
  988. await securityhub.batchImportFindings({
  989. Findings: event.Records.map(record => {
  990. const finding = {
  991. SchemaVersion: '2018-10-08',
  992. Id: record.Sns.MessageId,
  993. ProductArn: 'arn:${AWS::Partition}:securityhub:${AWS::Region}:${AWS::AccountId}:product/${AWS::AccountId}/default',
  994. GeneratorId: 'clamav',
  995. AwsAccountId: '${AWS::AccountId}',
  996. Types: ['Unusual Behaviors/Data'],
  997. CreatedAt: record.Sns.Timestamp,
  998. UpdatedAt: record.Sns.Timestamp,
  999. Severity: {
  1000. Product: 1,
  1001. Normalized: 70
  1002. },
  1003. Confidence: 100,
  1004. Title: generateTitle(record),
  1005. Description: generateDescription(record),
  1006. Resources: [{
  1007. Type: 'AwsS3Bucket',
  1008. Id: `arn:aws:s3:::${!record.Sns.MessageAttributes.bucket.Value}`
  1009. }]
  1010. };
  1011. return finding;
  1012. })
  1013. }).promise();
  1014. } catch (err) {
  1015. if (err.code === 'AccessDeniedException') { // Security Hub not enabled
  1016. return false;
  1017. } else {
  1018. throw err;
  1019. }
  1020. }
  1021. return true;
  1022. };
  1023. Handler: index.handler
  1024. MemorySize: 128
  1025. Role: !<!GetAtt> SecurityHubIntegrationLambdaRole.Arn
  1026. Runtime: nodejs8.10
  1027. Timeout: 60
  1028. SecurityHubIntegrationLambdaLogGroup:
  1029. Condition: HasSecurityHubIntegration
  1030. Type: 'AWS::Logs::LogGroup'
  1031. Properties:
  1032. LogGroupName: !<!Sub> '/aws/lambda/${SecurityHubIntegrationLambdaFunction}'
  1033. RetentionInDays: !<!Ref> LogsRetentionInDays
  1034. OpsCenterIntegrationSubscription:
  1035. Condition: HasOpsCenterIntegration
  1036. DependsOn:
  1037. - OpsCenterIntegrationLambdaPermission
  1038. - OpsCenterIntegrationLambdaPolicy
  1039. Type: 'AWS::SNS::Subscription'
  1040. Properties:
  1041. Endpoint: !<!GetAtt> OpsCenterIntegrationLambdaFunction.Arn
  1042. FilterPolicy:
  1043. status:
  1044. - infected
  1045. - 'no'
  1046. Protocol: lambda
  1047. TopicArn: !<!Ref> FindingsTopic
  1048. OpsCenterIntegrationLambdaPermission:
  1049. Condition: HasOpsCenterIntegration
  1050. Type: 'AWS::Lambda::Permission'
  1051. Properties:
  1052. Action: 'lambda:InvokeFunction'
  1053. FunctionName: !<!Ref> OpsCenterIntegrationLambdaFunction
  1054. Principal: sns.amazonaws.com
  1055. SourceArn: !<!Ref> FindingsTopic
  1056. OpsCenterIntegrationLambdaRole:
  1057. Condition: HasOpsCenterIntegration
  1058. Type: 'AWS::IAM::Role'
  1059. Properties:
  1060. AssumeRolePolicyDocument:
  1061. Version: 2012-10-17
  1062. Statement:
  1063. - Effect: Allow
  1064. Principal:
  1065. Service: lambda.amazonaws.com
  1066. Action: 'sts:AssumeRole'
  1067. Policies:
  1068. - PolicyName: ssm
  1069. PolicyDocument:
  1070. Statement:
  1071. - Effect: Allow
  1072. Action: 'ssm:CreateOpsItem'
  1073. Resource: '*'
  1074. OpsCenterIntegrationLambdaPolicy:
  1075. Condition: HasOpsCenterIntegration
  1076. Type: 'AWS::IAM::Policy'
  1077. Properties:
  1078. Roles:
  1079. - !<!Ref> OpsCenterIntegrationLambdaRole
  1080. PolicyName: logs
  1081. PolicyDocument:
  1082. Statement:
  1083. - Effect: Allow
  1084. Action:
  1085. - 'logs:CreateLogStream'
  1086. - 'logs:PutLogEvents'
  1087. Resource: !<!GetAtt> OpsCenterIntegrationLambdaLogGroup.Arn
  1088. OpsCenterIntegrationLambdaFunction:
  1089. Condition: HasOpsCenterIntegration
  1090. Type: 'AWS::Lambda::Function'
  1091. Properties:
  1092. Code:
  1093. ZipFile: |
  1094. 'use strict';
  1095. const AWS = require('aws-sdk');
  1096. const ssm = new AWS.SSM({apiVersion: '2018-10-26'});
  1097. function generateTitle(record) {
  1098. if (record.Sns.MessageAttributes.status.Value === 'no') {
  1099. return 'File scan failed';
  1100. } else if (record.Sns.MessageAttributes.status.Value === 'infected') {
  1101. return 'Infected file found';
  1102. } else {
  1103. throw new Error(`unsupported status: ${record.Sns.MessageAttributes.status.Value}`);
  1104. }
  1105. }
  1106. function generateDescription(record) {
  1107. if (record.Sns.MessageAttributes.status.Value === 'no') {
  1108. if ('version' in record.Sns.MessageAttributes) {
  1109. return `File in S3 bucket ${record.Sns.MessageAttributes.bucket.Value} with key ${record.Sns.MessageAttributes.key.Value} and version ${record.Sns.MessageAttributes.version.Value} could not be scanned, ${record.Sns.MessageAttributes.action.Value} action executed`;
  1110. } else {
  1111. return `File in S3 bucket ${record.Sns.MessageAttributes.bucket.Value} with key ${record.Sns.MessageAttributes.key.Value} could not be scanned, ${record.Sns.MessageAttributes.action.Value} action executed`;
  1112. }
  1113. } else if (record.Sns.MessageAttributes.status.Value === 'infected') {
  1114. if ('version' in record.Sns.MessageAttributes) {
  1115. return `Infected file found in S3 bucket ${record.Sns.MessageAttributes.bucket.Value} with key ${record.Sns.MessageAttributes.key.Value} and version ${record.Sns.MessageAttributes.version.Value}, ${record.Sns.MessageAttributes.action.Value} action executed`;
  1116. } else {
  1117. return `Infected file found in S3 bucket ${record.Sns.MessageAttributes.bucket.Value} with key ${record.Sns.MessageAttributes.key.Value}, ${record.Sns.MessageAttributes.action.Value} action executed`;
  1118. }
  1119. } else {
  1120. throw new Error(`unsupported status: ${record.Sns.MessageAttributes.status.Value}`);
  1121. }
  1122. }
  1123. exports.handler = async (event) => {
  1124. console.log(`Invoke: ${JSON.stringify(event)}`);
  1125. await Promise.all(event.Records.map(record => {
  1126. return ssm.createOpsItem({
  1127. Description: generateDescription(record),
  1128. OperationalData: {
  1129. '/aws/resources': {
  1130. Value: `[{"arn": "arn:aws:s3:::${record.Sns.MessageAttributes.bucket.Value}"}]`,
  1131. Type: 'SearchableString'
  1132. },
  1133. 'bucket': {
  1134. Value: record.Sns.MessageAttributes.bucket.Value,
  1135. Type: 'SearchableString'
  1136. },
  1137. 'key': {
  1138. Value: record.Sns.MessageAttributes.bucket.Value,
  1139. Type: 'SearchableString'
  1140. }
  1141. },
  1142. Priority: 4,
  1143. Source: 's3-virusscan',
  1144. Title: generateTitle(record),
  1145. }).promise();
  1146. }));
  1147. return true;
  1148. };
  1149. Handler: index.handler
  1150. MemorySize: 128
  1151. Role: !<!GetAtt> OpsCenterIntegrationLambdaRole.Arn
  1152. Runtime: nodejs8.10
  1153. Timeout: 60
  1154. OpsCenterIntegrationLambdaLogGroup:
  1155. Condition: HasOpsCenterIntegration
  1156. Type: 'AWS::Logs::LogGroup'
  1157. Properties:
  1158. LogGroupName: !<!Sub> '/aws/lambda/${OpsCenterIntegrationLambdaFunction}'
  1159. RetentionInDays: !<!Ref> LogsRetentionInDays
  1160. CloudWatchIntegrationSubscription:
  1161. Condition: HasCloudWatchIntegration
  1162. DependsOn:
  1163. - CloudWatchIntegrationLambdaPermission
  1164. - CloudWatchIntegrationLambdaPolicy
  1165. Type: 'AWS::SNS::Subscription'
  1166. Properties:
  1167. Endpoint: !<!GetAtt> CloudWatchIntegrationLambdaFunction.Arn
  1168. Protocol: lambda
  1169. TopicArn: !<!Ref> FindingsTopic
  1170. CloudWatchIntegrationLambdaPermission:
  1171. Condition: HasCloudWatchIntegration
  1172. Type: 'AWS::Lambda::Permission'
  1173. Properties:
  1174. Action: 'lambda:InvokeFunction'
  1175. FunctionName: !<!Ref> CloudWatchIntegrationLambdaFunction
  1176. Principal: sns.amazonaws.com
  1177. SourceArn: !<!Ref> FindingsTopic
  1178. CloudWatchIntegrationLambdaRole:
  1179. Condition: HasCloudWatchIntegration
  1180. Type: 'AWS::IAM::Role'
  1181. Properties:
  1182. AssumeRolePolicyDocument:
  1183. Version: 2012-10-17
  1184. Statement:
  1185. - Effect: Allow
  1186. Principal:
  1187. Service: lambda.amazonaws.com
  1188. Action: 'sts:AssumeRole'
  1189. Policies:
  1190. - PolicyName: ssm
  1191. PolicyDocument:
  1192. Statement:
  1193. - Effect: Allow
  1194. Action: 'cloudwatch:PutMetricData'
  1195. Resource: '*'
  1196. CloudWatchIntegrationLambdaPolicy:
  1197. Condition: HasCloudWatchIntegration
  1198. Type: 'AWS::IAM::Policy'
  1199. Properties:
  1200. Roles:
  1201. - !<!Ref> CloudWatchIntegrationLambdaRole
  1202. PolicyName: logs
  1203. PolicyDocument:
  1204. Statement:
  1205. - Effect: Allow
  1206. Action:
  1207. - 'logs:CreateLogStream'
  1208. - 'logs:PutLogEvents'
  1209. Resource: !<!GetAtt> CloudWatchIntegrationLambdaLogGroup.Arn
  1210. CloudWatchIntegrationLambdaFunction:
  1211. Condition: HasCloudWatchIntegration
  1212. Type: 'AWS::Lambda::Function'
  1213. Properties:
  1214. Code:
  1215. ZipFile: |
  1216. 'use strict';
  1217. const AWS = require('aws-sdk');
  1218. const cloudwatch = new AWS.CloudWatch({apiVersion: '2010-08-01'});
  1219. exports.handler = async (event) => {
  1220. console.log(`Invoke: ${JSON.stringify(event)}`);
  1221. const no = event.Records.filter(record => record.Sns.MessageAttributes.status.Value === 'no').length;
  1222. const clean = event.Records.filter(record => record.Sns.MessageAttributes.status.Value === 'clean').length;
  1223. const infected = event.Records.filter(record => record.Sns.MessageAttributes.status.Value === 'infected').length;
  1224. await cloudwatch.putMetricData({
  1225. Namespace: process.env.NAMESPACE,
  1226. MetricData: [{
  1227. MetricName: 'no',
  1228. Value: no,
  1229. Unit: 'Count'
  1230. }, {
  1231. MetricName: 'clean',
  1232. Value: clean,
  1233. Unit: 'Count'
  1234. }, {
  1235. MetricName: 'infected',
  1236. Value: infected,
  1237. Unit: 'Count'
  1238. }]
  1239. }).promise();
  1240. return true;
  1241. };
  1242. Environment:
  1243. Variables:
  1244. NAMESPACE: !<!Ref> 'AWS::StackName'
  1245. Handler: index.handler
  1246. MemorySize: 128
  1247. Role: !<!GetAtt> CloudWatchIntegrationLambdaRole.Arn
  1248. Runtime: nodejs8.10
  1249. Timeout: 60
  1250. CloudWatchIntegrationLambdaLogGroup:
  1251. Condition: HasCloudWatchIntegration
  1252. Type: 'AWS::Logs::LogGroup'
  1253. Properties:
  1254. LogGroupName: !<!Sub> '/aws/lambda/${CloudWatchIntegrationLambdaFunction}'
  1255. RetentionInDays: !<!Ref> LogsRetentionInDays
  1256. Outputs:
  1257. Version:
  1258. Description: S3 VirusScan version.
  1259. Value: 1.0.0
  1260. StackName:
  1261. Description: Stack name.
  1262. Value: !<!Sub> '${AWS::StackName}'
  1263. ScanQueueArn:
  1264. Description: The ARN of the Scan Queue.
  1265. Value: !<!GetAtt> ScanQueue.Arn
  1266. Export:
  1267. Name: !<!Sub> '${AWS::StackName}-ScanQueueArn'
  1268. ScanQueueName:
  1269. Description: The name of the Scan Queue.
  1270. Value: !<!GetAtt> ScanQueue.QueueName
  1271. Export:
  1272. Name: !<!Sub> '${AWS::StackName}-ScanQueueName'
  1273. ScanQueueUrl:
  1274. Description: The URL of the Scan Queue.
  1275. Value: !<!Ref> ScanQueue
  1276. Export:
  1277. Name: !<!Sub> '${AWS::StackName}-ScanQueueUrl'
  1278. FindingsTopicArn:
  1279. Description: The ARN of the Findings Topic.
  1280. Value: !<!Ref> FindingsTopic
  1281. Export:
  1282. Name: !<!Sub> '${AWS::StackName}-FindingsTopicArn'
  1283. FindingsTopicName:
  1284. Description: The name of the Findings Topic.
  1285. Value: !<!GetAtt> FindingsTopic.TopicName
  1286. Export:
  1287. Name: !<!Sub> '${AWS::StackName}-FindingsTopicName'
Add Comment
Please, Sign In to add comment