├── AWS_logging.md ├── GCP ├── skunkworks-project.tf └── variables.tf ├── README.md ├── cf └── security.global.cf │ ├── README.md │ ├── awsconfig.global.json │ ├── cloudtrail.global.json │ ├── cloudtrailalarms.global.json │ ├── iam.global.json │ └── security.global.yaml ├── scripts.aws ├── aws_enforce_password_policy.py ├── aws_secgroup_viewer.py ├── aws_test_bucket.py └── s3_enc_check.py ├── selfdefence.PoC.cf ├── selfdefence.infosec.vpc.json └── selfdefence_infosec.py └── tf ├── infosec.tf └── terraform.tfvars /AWS_logging.md: -------------------------------------------------------------------------------- 1 | # Native AWS logging capabilities 2 | 3 | Unofficial documentation on the AWS services logging capabilities. Structured and enhanced. 4 | 5 | official doc (missing a lot of services): https://aws.amazon.com/answers/logging/aws-native-security-logging-capabilities/ 6 | 7 | * [CloudTrail](#cloudtrail) 8 | * [VPC Flow Logs](#vpcflowlogs) 9 | * [S3 Server Access Logs](#s3accesslogs) 10 | * [Elastic Load Balancer(ELB) logs (classic)](#elblogs) 11 | * [Network Load Balancer(NLB) logs](#nlblogs) 12 | 13 | 14 | 15 | * [CloudWatch Logs](#cloudwatchlogs) 16 | 17 | ## CloudTrail 18 | * Log coverage: 19 | * all AWS API calls (covers web-ua, api or SDK actions) 20 | * List of the services covered by cloudtrail 21 | https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-aws-service-specific-topics.html 22 | * Options: 23 | 1. A trail that applies to all regions 24 | 2. A trail that applies to one region 25 | 3. organization trail - global for all subaccount in organization 26 | * Exceptions and Limits: 27 | * Do not cover newest services/and api calls: 28 | * List of the uncovered services: 29 | https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-unsupported-aws-services.html 30 | * Log record/file format: 31 | * json, structure is service-specific 32 | * Delivery latency: 33 | * Within 15 min of the activity 34 | * Transport/Encryption in transit: 35 | * internal to AWS 36 | * Supported log Destinations: 37 | * CloudWatch Logs 38 | * S3 bucket 39 | * Encryption at rest: 40 | * S3 - AES256, S3 SSE with amazon keys or KMS 41 | * Data residency(AWS Region): 42 | * S3 bucket from any region. 43 | * Retention capabilities: 44 | * AWS Cloudtrail Console (EventHistory) - 90 days 45 | * S3 -indefinite time/user defined 46 | * CloudWatch Logs: indefinitely and never expire. User can define retention policy per log group (indefinite, or from 1 day to 10years) 47 | 48 | ## VPC Flow logs 49 | * Log coverage: 50 | * VPC 51 | * Subnet 52 | * Network interface 53 | * accepted traffic, rejected traffic, or all traffic 54 | https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html 55 | * Exceptions and Limits: 56 | * https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-logs-limitations 57 | * Flow logs do not capture all IP traffic. The following types of traffic are not logged: 58 | * Traffic generated by instances when they contact the Amazon DNS server. If you use your own DNS server, then all traffic to that DNS server is logged. 59 | * Traffic generated by a Windows instance for Amazon Windows license activation. 60 | * Traffic to and from 169.254.169.254 for instance metadata. 61 | * Traffic to and from 169.254.169.123 for the Amazon Time Sync Service. 62 | * DHCP traffic. 63 | * Traffic to the reserved IP address for the default VPC router. For more information, see VPC and Subnet Sizing. 64 | * Traffic between an endpoint network interface and a Network Load Balancer network interface. For more information, see VPC Endpoint Services (AWS PrivateLink). 65 | * Log record/file format: 66 | * space-separated string 67 | * Format details: 68 | * A flow log record represents a network flow in your flow log. Each record captures the network flow for a specific 5-tuple, for a specific capture window. A 5-tuple is a set of five different values that specify the source, destination, and protocol for an internet protocol (IP) flow. The capture window is a duration of time during which the flow logs service aggregates data before publishing flow log records. 69 | * https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-log-records 70 | * Fields of a flow log record: 71 | * version: The VPC Flow Logs version. 72 | * account-id: The AWS account ID for the flow log. 73 | * interface-id: The ID of the network interface for which the traffic is recorded. 74 | * srcaddr: The source IPv4 or IPv6 address. The IPv4 address of the network interface is always its private IPv4 address. 75 | * dstaddr: The destination IPv4 or IPv6 address. The IPv4 address of the network interface is always its private IPv4 address. 76 | * srcport: The source port of the traffic. 77 | * dstport: The destination port of the traffic. 78 | * protocol: The IANA protocol number of the traffic. For more information, see Assigned Internet Protocol Numbers. 79 | * packets: The number of packets transferred during the capture window. 80 | * bytes: The number of bytes transferred during the capture window. 81 | * start: The time, in Unix seconds, of the start of the capture window. 82 | * end: The time, in Unix seconds, of the end of the capture window. 83 | * action: The action associated with the traffic: 84 | * ACCEPT: The recorded traffic was permitted by the security groups or network ACLs. 85 | * REJECT: The recorded traffic was not permitted by the security groups or network ACLs. 86 | * log-status The logging status of the flow log: 87 | * OK: Data is logging normally to the chosen destinations. 88 | * NODATA: There was no network traffic to or from the network interface during the capture window. 89 | * SKIPDATA: Some flow log records were skipped during the capture window. This may be because of an internal capacity constraint, or an internal error. 90 | * Delivery latency: 91 | * each capture window: ~10 min 92 | * max 15 min 93 | * Transport/Encryption in transit: 94 | * internal to AWS 95 | * Supported log Destinations: 96 | * CloudWatch Logs 97 | * S3 bucket 98 | * Encryption at rest: 99 | * S3 - AES256, S3 SSE with amazon keys or KMS 100 | * Data residency(AWS Region): 101 | * S3 bucket from any region. 102 | * Retention capabilities: 103 | * S3 -indefinite time/user defined 104 | * CloudWatch Logs: indefinitely and never expire. User can define retention policy per log group (indefinite, or from 1 day to 10years) 105 | 106 | 107 | ## S3 bucket access logs (S3 Server Access Logs) 108 | 109 | * Log coverage: 110 | * Logs all requests to the data in the bucket 111 | * https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html 112 | * Exceptions and Limits: 113 | * Not guaranteed delivery 114 | * Log record/file format: 115 | * Newline-delimited log records, Each log record represents one request and consists of space-delimited fields 116 | * Each access log record provides details about a single access request, such as the requester, bucket name, request time, request action, response status, and an error code, if relevant. 117 | * https://docs.aws.amazon.com/AmazonS3/latest/dev/LogFormat.html 118 | * Fields: 119 | 1. Bucket Owner 120 | 2. Bucket 121 | 3. Time 122 | 4. Remote IP 123 | 5. Requester 124 | 6. Request ID 125 | 7. Operation 126 | 8. Key 127 | 9. Request-URI 128 | 10. HTTP status 129 | 11. Error Code 130 | 12. Bytes Sent 131 | 13. Object Size 132 | 14. Total Time 133 | 15. Turn-Around Time 134 | 16. Referrer 135 | 17. User-Agent 136 | 18. Version Id 137 | * Delivery latency: 138 | * delivered on a best effort basis - normally hours. 139 | * Transport/Encryption in transit: 140 | * internal to AWS 141 | * Supported log Destinations: 142 | * S3 bucket, same as source bucket(with prefix) or different one 143 | * Encryption at rest: 144 | * S3 - AES256, S3 SSE with amazon keys or KMS 145 | * Data residency(AWS Region): 146 | * Same Region as the source bucket, bucket must be owned by the same AWS account 147 | * Retention capabilities: 148 | * S3 -indefinite time/user defined 149 | 150 | ## Elastic Load Balancer(ELB) logs (classic) 151 | * Log coverage: 152 | * access logs capture detailed information about requests sent to your load balancer. Each log contains information such as the time the request was received, the client's IP address, latencies, request paths, and server responses 153 | * https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/access-log-collection.html 154 | * Exceptions and Limits: 155 | * Note: Elastic Load Balancing logs requests sent to the load balancer, including requests that never made it to the back-end instances 156 | ​ 157 | * Limits: Elastic Load Balancing logs requests on a best-effort basis. We recommend that you use access logs to understand the nature of the requests, not as a complete accounting of all requests. 158 | * Log record/file format: 159 | * Each log entry contains the details of a single request made to the load balancer. All fields in the log entry are delimited by spaces. 160 | * Format: 161 | 1. timestamp 162 | 2. elb client:port 163 | 3. backend:port 164 | 4. request_processing_time 165 | 5. backend_processing_time 166 | 6. response_processing_time 167 | 7. elb_status_code 168 | 8. backend_status_code 169 | 9. received_bytes 170 | 10. sent_bytes 171 | 11. "request" 172 | 12. "user_agent" 173 | 13. ssl_cipher 174 | 14. ssl_protocol 175 | * Delivery latency: 176 | * User defined publishing interval 177 | (5 min-60 min) 178 | * Transport/Encryption in transit: 179 | * internal to AWS 180 | * Supported log Destinations: 181 | * S3 bucket 182 | * Encryption at rest: 183 | * * S3 - AES256, S3 SSE with amazon keys 184 | * Data residency(AWS Region): 185 | * As per S3 bucket location 186 | * Retention capabilities: 187 | * S3 -indefinite time/user defined 188 | 189 | ## Network Load Balancer(NLB) Logs 190 | * Log coverage: 191 | * Access logs that capture detailed information about the TLS requests sent to your Network Load Balancer. 192 | * https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-access-logs.html 193 | 194 | * Exceptions and Limits: 195 | * Access logs are created only if the load balancer has a TLS listener and they contain information only about TLS requests. 196 | * Log record/file format: 197 | * All fields are delimited by spaces. When new fields are introduced, they are added to the end of the log entry. 198 | * Log file name format: *bucket[/prefix]/AWSLogs/aws-account-id/elasticloadbalancing/region/yyyy/mm/dd/aws-account-id_elasticloadbalancing_region_load-balancer-id_end-time_random-string.log.gz* 199 | * Access log fields: 200 | * type: The type of listener. The supported value is tls. 201 | * version: The version of the log entry. The supported version is 1.0. 202 | * timestamp: The timestamp recorded at the end of the TLS connection, in ISO 8601 format. 203 | * elb: The resource ID of the load balancer. 204 | * listener: The resource ID of the TLS listener for the connection. 205 | * client:port : The IP address and port of the client. 206 | * listener:port : The IP address and port of the listener. 207 | * connection_time: The total time for the connection to complete, from start to closure, in milliseconds. 208 | * tls_handshake_time: The total time for the TLS handshake to complete after the TCP connection is established, including client-side delays, in milliseconds. This time is included in the connection_time field. 209 | * received_bytes: The count of bytes received by the load balancer from the client, after decryption. 210 | * sent_bytes: The count of bytes sent by the load balancer to the client, before encryption. 211 | * incoming_tls_alert: The integer value of TLS alerts received by the load balancer from the client, if present. Otherwise, this value is set to -. 212 | * chosen_cert_arn: The ARN of the certificate served to the client. If no valid client hello message is sent, this value is set to -. 213 | * chosen_cert_serial: Reserved for future use. This value is always set to -. 214 | * tls_cipher: The cipher suite negotiated with the client, in OpenSSL format. If TLS negotiation does not complete, this value is set to -. 215 | * tls_protocol_version: The TLS protocol negotiated with the client, in string format. The possible values are tlsv10, tlsv11, and tlsv12. If TLS negotiation does not complete, this value is set to -. 216 | * tls_named_group: Reserved for future use. This value is always set to -. 217 | * domain_name: The value of the server_name extension in the client hello message. This value is URL-encoded. If no valid client hello message is sent or the extension is not present, this value is set to -. 218 | * Delivery latency: 219 | * each 5 min 220 | * Transport/Encryption in transit: 221 | * internal to AWS 222 | * Supported log Destinations: 223 | * S3 bucket 224 | * Encryption at rest: 225 | * * S3 - AES256, S3 SSE with amazon keys 226 | * Data residency(AWS Region): 227 | * As per S3 bucket location 228 | * The bucket must be located in the same region as the load balancer. 229 | * Retention capabilities: 230 | * S3 -indefinite time/user defined 231 | 232 | ## Application Load Balancer(ALB) logs 233 | * Log coverage: 234 | * Access Log capture detailed information about requests sent to your load balancer. Each log contains information such as the time the request was received, the client's IP address, latencies, request paths, and server responses 235 | * https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html 236 | * Exceptions and Limits: 237 | * Note: Elastic Load Balancing does not log health check requests 238 | * Limits: Elastic Load Balancing logs requests on a best-effort basis. We recommend that you use access logs to understand the nature of the requests, not as a complete accounting of all requests. 239 | * Log record/file format: 240 | * Each log entry contains the details of a single request (or connection in the case of WebSockets) made to the load balancer. For WebSockets, an entry is written only after the connection is closed. If the upgraded connection can't be established, the entry is the same as for an HTTP or HTTPS request. 241 | All fields are delimited by spaces. When new fields are introduced, they are added to the end of the log entry. 242 | * Delivery latency: 243 | * each 5 min 244 | * Transport/Encryption in transit: 245 | * internal to AWS 246 | * Supported log Destinations: 247 | * S3 bucket 248 | * Encryption at rest: 249 | * * S3 - AES256, S3 SSE with amazon keys 250 | * Data residency(AWS Region): 251 | * As per S3 bucket location 252 | * Retention capabilities: 253 | * S3 -indefinite time/user defined 254 | 255 | ## Route53 DNS request 256 | * Log coverage: 257 | * log information about the queries that Route 53 receives. 258 | The domain or subdomain that was requested 259 | The date and time of the request 260 | The DNS record type (such as A or AAAA) 261 | The Route 53 edge location that responded to the DNS query 262 | The DNS response code, such as NoError or ServFail 263 | * Exceptions and Limits: 264 | * Query logging is available only for public hosted zones 265 | * cached response will not be logged 266 | 267 | * Log record/file format: 268 | * newline-delimited log records, Each log record represents one request and consists of space-delimited fields 269 | * https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/query-logs.html#query-logs-format 270 | * Fields: 271 | 1. Log format version 272 | 2. Query timestamp 273 | 3. Hosted zone ID 274 | 4. Query name 275 | 5. Query type 276 | 6. Response code 277 | 7. Layer 4 protocol 278 | 8. Route 53 edge location 279 | 9. Resolver IP address 280 | 10. EDNS client subnet 281 | * Delivery latency: 282 | * N/A 283 | * Transport/Encryption in transit: 284 | * Supported log Destinations: 285 | * CloudWatch Logs 286 | * Encryption at rest: 287 | * As per CloudWatchLogs configuration 288 | * Data residency(AWS Region): 289 | * US East (N. Virginia) Region 290 | * Retention capabilities: 291 | * CloudWatch logs: indefinite time/user defined 292 | 293 | ## Lambda 294 | * Log coverage: 295 | * Lambda logs all requests handled by your function and also automatically stores logs generated by your code through Amazon CloudWatch Logs 296 | * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-functions-logs.html 297 | * You can insert logging statements into your code to help you validate that your code is working as expected. Lambda automatically integrates with CloudWatch Logs and pushes all logs from your code to a CloudWatch Logs group associated with a Lambda function 298 | * Exceptions and Limits: 299 | * Log record/file format: 300 | JSON? 301 | * Delivery latency: 302 | * as per CloudWatchLogs 303 | * Transport/Encryption in transit: 304 | * as per CloudWatchLogs 305 | * Supported log Destinations: 306 | * CloudWatchLogs 307 | * Encryption at rest: 308 | * as per CloudWatchLogs 309 | * Data residency(AWS Region): 310 | * any region 311 | * Retention capabilities: 312 | * CloudWatch logs: indefinite time/user defined 313 | 314 | ## CloudFront Access Logs 315 | * Log coverage: 316 | * Logs every user request that CloudFront receives. These access logs are available for both web and RTMP distributions 317 | * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html 318 | * Exceptions and Limits: 319 | * Note, however, that some or all log file entries for a time period can sometimes be delayed by up to 24 hours 320 | * Log record/file format: 321 | * Web Distribution Log File Format 322 | RTMP Distribution Log File Format 323 | Each entry in a log file gives details about a single user request. The log files for web and for RTMP distributions are not identical, but they share the following characteristics: 324 | Use the W3C extended log file format. (For more information, go to http://www.w3.org/TR/WD-logfile.html.) 325 | Contain tab-separated values. 326 | Contain records that are not necessarily in chronological order. 327 | Contain two header lines: one with the file-format version, and another that lists the W3C fields included in each record. 328 | Substitute URL-encoded equivalents for spaces and non-standard characters in field values. 329 | * Delivery latency: 330 | * up to several times an hour 331 | * Transport/Encryption in transit: 332 | * internal to AWS 333 | * Supported log Destinations: 334 | * S3 bucket 335 | * Encryption at rest: 336 | * * S3 - AES256, S3 SSE with amazon keys 337 | * Data residency(AWS Region): 338 | * As per S3 bucket location 339 | * Retention capabilities: 340 | * S3 -indefinite time/user defined 341 | 342 | ## Amazon Redshift Logs 343 | * Log coverage: 344 | * Amazon Redshift logs information about connections and user activities in your database. 345 | * https://docs.aws.amazon.com/redshift/latest/mgmt/db-auditing.html 346 | * Exceptions and Limits: 347 | * Log record/file format: 348 | * Amazon Redshift logs information in the following log files: 349 | 1. Connection log — logs authentication attempts, and connections and disconnections. 350 | 2. User log — logs information about changes to database user definitions. 351 | 3. User activity log — logs each query before it is run on the database. 352 | ​ 353 | * Logs format for each of the logs files can be found here: https://docs.aws.amazon.com/redshift/latest/mgmt/db-auditing.html#db-auditing-logs 354 | * Delivery latency: 355 | * depends on the Redshift cluster load. More load - more often you get logs 356 | * Transport/Encryption in transit: 357 | * internal to AWS 358 | * Supported log Destinations: 359 | * S3 bucket 360 | * Encryption at rest: 361 | * * S3 - AES256, S3 SSE with amazon keys 362 | * Data residency(AWS Region): 363 | * As per S3 bucket location 364 | * Retention capabilities: 365 | * S3 -indefinite time/user defined 366 | 367 | ## Amazon RDS Database Log 368 | * Log coverage: 369 | * Amazon RDS Database Logs are specific to the database engine: 370 | * https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.html 371 | * Exceptions and Limits: 372 | * Log record/file format: 373 | * DataBase engine specific: 374 | 1. MariaDB Database Log Files 375 | 2. Microsoft SQL Server Database Log Files 376 | 3. MySQL Database Log Files 377 | 4. Oracle Database Log Files 378 | 5. PostgreSQL Database Log Files 379 | * Delivery latency: 380 | * DB engine specific 381 | * Transport/Encryption in transit: 382 | * Supported log Destinations: 383 | * On DB servers itself 384 | * CloudWatchLogs 385 | * Encryption at rest: 386 | * As per DB instance encryption - AES-256 encryption 387 | * As per CloudWatchLogs configuration 388 | * Data residency(AWS Region): 389 | * Retention capabilities: 390 | * DB-stored logs retention depends on the Db engine (3-7 days) 391 | * CloudWatch logs: indefinite time/user defined 392 | 393 | ## Kinesis Data Firehose 394 | * Log coverage: 395 | * Kinesis Data Firehose integrates with Amazon CloudWatch Logs so that you can view the specific error logs when the Lambda invocation for data transformation or data delivery fails 396 | * https://docs.aws.amazon.com/firehose/latest/dev/monitoring-with-cloudwatch-logs.html 397 | * Exceptions and Limits: 398 | * Log record/file format: 399 | * two log streams named S3Delivery and RedshiftDelivery: S3Delivery log stream is used for logging errors related to delivery failure to the intermediate S3 bucket. The RedshiftDelivery log stream is used for logging errors related to Lambda invocation failure and delivery failure to your Amazon Redshift cluster. 400 | * Data Delivery Errors: 401 | 1. Amazon S3 Data Delivery Errors 402 | 2. Amazon Redshift Data Delivery Errors 403 | 3. Splunk Data Delivery Errors 404 | 4. Amazon Elasticsearch Service Data Delivery Errors 405 | 5. Lambda Invocation Errors 406 | * Delivery latency: 407 | * Transport/Encryption in transit: 408 | * internal to AWS 409 | * Supported log Destinations: 410 | * CloudWatch Logs 411 | * Encryption at rest: 412 | * As per CloudWatchLogs configuration 413 | * Data residency(AWS Region): 414 | * Retention capabilities: 415 | * CloudWatch logs: indefinite time/user defined 416 | 417 | ## Amazon ECS (AWS Fargate) 418 | * Log coverage: 419 | * You can configure the containers in your tasks to send log information to CloudWatch Logs. This allows you to view the logs from the containers in your Fargate tasks. 420 | * https://docs.aws.amazon.com/AmazonECS/latest/userguide/using_awslogs.html 421 | * Exceptions and Limits: 422 | * The type of information that is logged by your task's containers depends mostly on their ENTRYPOINT command. By default, the logs that are captured show the command output that you would normally see in an interactive terminal if you ran the container locally, which are the STDOUT and STDERR I/O streams. 423 | * Log record/file format: 424 | * STDOUT and STDERR I/O streams 425 | * Delivery latency: 426 | * Transport/Encryption in transit: 427 | * internal to AWS 428 | * Supported log Destinations: 429 | * CloudWatch Logs 430 | * Encryption at rest: 431 | * As per CloudWatchLogs configuration 432 | * Data residency(AWS Region): 433 | * Retention capabilities: 434 | * CloudWatch logs: indefinite time/user defined 435 | 436 | ## AWS WAF 437 | * Log coverage: 438 | * You can enable logging to get detailed information about traffic that is analyzed by your web ACL. Information that is contained in the logs include the time that AWS WAF received the request from your AWS resource, detailed information about the request, and the action for the rule that each request matched. 439 | * https://docs.aws.amazon.com/waf/latest/developerguide/logging.html 440 | * Exceptions and Limits: 441 | * Log record/file format: 442 | * json 443 | * One AWS WAF log is equivalent to one Kinesis Data Firehose record. 444 | * Delivery latency: 445 | * near real time 446 | * Transport/Encryption in transit: 447 | * Supported log Destinations: 448 | * Amazon Kinesis Data Firehose 449 | * Encryption at rest: 450 | * as for Amazon Kinesis Data Firehose 451 | * Data residency(AWS Region): 452 | * as for Amazon Kinesis Data Firehose 453 | * Retention capabilities: 454 | * as for Amazon Kinesis Data Firehose 455 | 456 | ## API Gateway 457 | * Log coverage: 458 | * Logs API requests and responses 459 | * https://docs.aws.amazon.com/apigateway/latest/developerguide/view-cloudwatch-log-events-in-cloudwatch-console.html 460 | * Exceptions and Limits: 461 | * Note: API Gateway creates log groups or log streams for an API stage at the time when it is deployed 462 | * Log record/file format: 463 | * Delivery latency: 464 | * Transport/Encryption in transit: 465 | * internal to AWS 466 | * Supported log Destinations: 467 | * CloudWatch Logs 468 | * Encryption at rest: 469 | * As per CloudWatchLogs configuration 470 | * Data residency(AWS Region): 471 | * Retention capabilities: 472 | * CloudWatch logs: indefinite time/user defined 473 | 474 | ## AWS Systems Manager 475 | * Log coverage: 476 | * AWS Systems Manager Agent is Amazon software that runs on your Amazon EC2 instances and your hybrid instances that are configured for Systems Manager (hybrid instances). 477 | you can configure SSM Agent to send log data to Amazon CloudWatch Logs 478 | * https://docs.aws.amazon.com/systems-manager/latest/userguide/monitoring-ssm-agent.html 479 | * Exceptions and Limits: 480 | * Note: The unified CloudWatch Agent has replaced SSM Agent as the tool for sending log data to Amazon CloudWatch Logs 481 | * Log record/file format: 482 | * system specific logs 483 | * Delivery latency: 484 | * s per agent settings 485 | * Transport/Encryption in transit: 486 | * internal to AWS 487 | * Supported log Destinations: 488 | * CloudWatch Logs 489 | * Encryption at rest: 490 | * As per CloudWatchLogs configuration 491 | * Data residency(AWS Region): 492 | * Retention capabilities: 493 | * CloudWatch logs: indefinite time/user defined 494 | 495 | ## Amazon EMR 496 | * Log coverage: 497 | * Amazon EMR and Hadoop both produce log files that report status on the cluster. 498 | There are many types of logs written to the master node. Amazon EMR writes step, bootstrap action, and instance state logs. Apache Hadoop writes logs to report the processing of jobs, tasks, and task attempts. Hadoop also records logs of its daemons. 499 | * https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-manage-view-web-log-files.html 500 | * Exceptions and Limits: 501 | * By default, Amazon EMR clusters launched using the console automatically archive log files to Amazon S3. You can specify your own log path, or you can allow the console to automatically generate a log path for you. For clusters launched using the CLI or API, you must configure Amazon S3 log archiving manually. 502 | * Log record/file format: 503 | * Apache Hadoop specific logs 504 | * http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/ClusterSetup.html 505 | * Amazon EMR writes step, bootstrap action, and instance state logs 506 | * Delivery latency: 507 | * as logs created 508 | * Transport/Encryption in transit: 509 | * local to host 510 | * Supported log Destinations: 511 | * Master node 512 | * S3 513 | * Encryption at rest: 514 | * EMR encryption: 515 | https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-data-encryption-options.html 516 | * AES256 Encryption: Amazon S3 server-side encryption (SSE) 517 | * Data residency(AWS Region): 518 | * As per node location 519 | * As per S3 bucket location 520 | * Retention capabilities: 521 | * Instance lifetime 522 | * S3: indefinite time/user defined 523 | 524 | ## Elastic Beanstalk 525 | * Log coverage: 526 | * The Amazon EC2 instances in your Elastic Beanstalk environment generate logs that you can view to troubleshoot issues with your application or configuration files. Logs created by the web server, application server, Elastic Beanstalk platform scripts, and AWS CloudFormation are stored locally on individual instances 527 | * https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.logging.html 528 | * Exceptions and Limits: 529 | * Log record/file format: 530 | * These logs contain messages about deployment activities, including messages related to configuration files (.ebextensions). 531 | * Linux 532 | * /var/log/eb-activity.log 533 | * /var/log/eb-commandprocessor.log 534 | * /var/log/eb-version-deployment.log 535 | * Windows Server 536 | * C:\Program Files\Amazon\ElasticBeanstalk\logs\ 537 | * C:\cfn\logs\cfn-init.log 538 | * Each application and web server stores logs in its own folder: 539 | * Apache – /var/log/httpd/ 540 | * IIS – C:\inetpub\wwwroot\ 541 | * Node.js – /var/log/nodejs/ 542 | * nginx – /var/log/nginx/ 543 | * Passenger – /var/app/support/logs/ 544 | * Puma – /var/log/puma/ 545 | * Python – /opt/python/log/ 546 | * Tomcat – /var/log/tomcat8/ 547 | * Delivery latency: 548 | * as logs created - near real time 549 | * Transport/Encryption in transit: 550 | * locally on the instance 551 | * logrotate to S3 552 | * Supported log Destinations: 553 | * instance 554 | * CloudWatch Logs 555 | * S3 556 | * Encryption at rest: 557 | * Instance encryption 558 | * As per CloudWatchLogs configuration (see below) 559 | * AES256 Encryption: Amazon S3 server-side encryption (SSE) 560 | * Data residency(AWS Region): 561 | * As per instance location 562 | * any region 563 | * As per S3 bucket location 564 | * Retention capabilities: 565 | * Instance lifetime 566 | * CloudWatch logs: indefinite time/user defined 567 | * Elastic Beanstalk deletes tail and bundle logs from Amazon S3 automatically 15 minutes after they are created. Rotated logs persist. 568 | 569 | ## OpsWorks 570 | * Log coverage: 571 | * To simplify the process of monitoring logs on multiple instances, AWS OpsWorks Stacks supports Amazon CloudWatch Logs 572 | * https://docs.aws.amazon.com/opsworks/latest/userguide/monitoring-cloudwatch-logs.html 573 | * Exceptions and Limits: 574 | * Log record/file format: 575 | * OS/app specifi 576 | * Delivery latency: 577 | * as per AWS OpsWorks Stacks agent 578 | * Transport/Encryption in transit: 579 | * internal to AWS 580 | * Supported log Destinations: 581 | * CloudWatch Logs 582 | * Encryption at rest: 583 | * As per CloudWatchLogs configuration 584 | * Data residency(AWS Region): 585 | * Retention capabilities: 586 | * CloudWatch logs: indefinite time/user defined 587 | 588 | 589 | # AWS Built-in Centralized logging capabilities 590 | ## Amazon CloudWatch Logs Service 591 | 592 | Amazon CloudWatch Logs could be used to monitor, store, and access your log files from Amazon Elastic Compute Cloud (Amazon EC2) instances, AWS CloudTrail, Route 53, and other sources. You can then retrieve the associated log data from CloudWatch Logs. 593 | https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html 594 | 595 | **Service coverage:** 596 | 597 | * Logs from Amazon EC2 Instances 598 | * *Logs Retrieval:* AWS provide agent (several different version are available) 599 | * *Delivery schedule:* as per agent settings 600 | * *Data residency:* any region 601 | * Route 53 DNS Queries 602 | * *Logs Retrieval:* Service push 603 | * *Delivery schedule:* 604 | * *Data residency:* US East (N. Virginia) Region only 605 | * VPC Flow Logs: 606 | * *Logs Retrieval:* Service push: https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html 607 | * *Delivery schedule:* 608 | * *Data residency:* 609 | * Lambda function Logs 610 | * *Logs Retrieval:* Service push 611 | * *Delivery schedule:* 612 | * *Data residency:* any aws region 613 | * Amazon RDS Database Log 614 | * *Logs Retrieval:* Service push 615 | * *Delivery schedule:* as per DB engine configuration 616 | * *Data residency:* any aws region 617 | * Kinesis Data Firehose 618 | * *Logs Retrieval:* Service push 619 | * *Delivery schedule:* 620 | * *Data residency:* any aws region 621 | * Amazon ECS (AWS Fargate) 622 | * *Logs Retrieval:* Service push 623 | * *Delivery schedule:* 624 | * *Data residency:* any aws region 625 | * API Gateway 626 | * *Logs Retrieval:* Service push 627 | * *Delivery schedule:* 628 | * *Data residency:* any aws region 629 | * AWS Systems Manager 630 | * *Logs Retrieval:* Agent push 631 | * *Delivery schedule:* as per agent configuration 632 | * *Data residency:* any aws region 633 | * Elastic Beanstalk 634 | * *Logs Retrieval:* Agent push 635 | * *Delivery schedule:* as per agent/service configuration 636 | * *Data residency:* any aws region 637 | * OpsWorks 638 | * *Logs Retrieval:* OpsWorks Stacks agent push 639 | * *Delivery schedule:* as per agent configuration 640 | * *Data residency:* any aws region 641 | * AWS Global Accelerator flow logs 642 | * *Logs Retrieval:* Service push through s3 : https://docs.aws.amazon.com/global-accelerator/latest/dg/monitoring-global-accelerator.flow-logs.html#monitoring-global-accelerator.flow-logs-publishing-S3 643 | * *Delivery schedule:* as per agent configuration 644 | * *Data residency:* any aws region 645 | 646 | **Encryption at REST:** 647 | 648 | AWS Key Management Service (AWS KMS) key: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html 649 | 650 | **Retention policy:** 651 | 652 | CloudWatch logs: logs are kept indefinitely and never expire. User can create retention policy per log group with following option: indefinite, or from 1 day to 10years 653 | Data visualization and analyzes: 654 | CloudWatch Logs Insights: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html 655 | Metric filters: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/MonitoringLogData.html 656 | 657 | **Notification and alerting:** 658 | 659 | CloudWatch Alarms: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html 660 | 661 | **Real time processing options:** 662 | Real-time Processing of Log Data with Subscriptions: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Subscriptions.html 663 | Supported AWS Services for the real time data processing: AWS Kinesis, Lambda 664 | 665 | **Data export and external tool integrations:** 666 | * Export data to S3: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/S3Export.html 667 | * Streaming data to the Amazon Elasticsearch Service: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_ES_Stream.html 668 | **Known limits:** 669 | 670 | Amazon CloudWatch Logs limits: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html 671 | 672 | **AWS recommended solutions/implementations**: 673 | 674 | https://aws.amazon.com/answers/logging/centralized-logging/ 675 | ​ 676 | 677 | 678 | 679 | 680 | 681 | 682 | # Templates 683 | 684 | ## Serice Template: 685 | * Log coverage: 686 | * Exceptions and Limits: 687 | * Log record/file format: 688 | * Delivery latency: 689 | * Transport/Encryption in transit: 690 | * Supported log Destinations: 691 | * Encryption at rest: 692 | * Data residency(AWS Region): 693 | * Retention capabilities: 694 | 695 | ## Cloudwatchlogs service template 696 | * *Logs Retrieval:* 697 | * *Delivery schedule:* 698 | * *Data residency:* -------------------------------------------------------------------------------- /GCP/skunkworks-project.tf: -------------------------------------------------------------------------------- 1 | variable "skunkworks-project_name" {default="skunkworks-project"} 2 | 3 | 4 | # ------------------------------------------------ 5 | # Creating required usrs and groups inside G-Suite 6 | #------------------------------------------------- 7 | 8 | 9 | provider "gsuite" { 10 | credentials = "/Users/thor/.config/gcloud/infosec-terraform-admin.json" 11 | impersonated_user_email = "ingvar_ua@company-test.com" 12 | oauth_scopes = [ 13 | "https://www.googleapis.com/auth/admin.directory.group", 14 | "https://www.googleapis.com/auth/admin.directory.user" 15 | ] 16 | } 17 | 18 | resource "gsuite_group" "skunkworks-team" { 19 | email = "skunkworks-team@company-test.com" 20 | name = "skunkworks-team@company-test.com" 21 | description = "skunkworks team" 22 | } 23 | 24 | # Creating users : 25 | 26 | # Creating Ivan Mazepa user 27 | resource "gsuite_user" "ivan_mazepa" { 28 | # advise to set this field to true on creation, then false afterwards 29 | change_password_next_login = false 30 | name { 31 | family_name = "Mazepa" 32 | given_name = "Ivan" 33 | } 34 | # on creation this field is required 35 | # password = "7864648UI&83g*&^89te" 36 | primary_email = "ivan.mazepa@company-test.com" 37 | } 38 | 39 | resource "gsuite_group_member" "ivan_mazepa" { 40 | group = "${gsuite_group.skunkworks-team.email}" 41 | email = "${gsuite_user.ivan_mazepa.primary_email}" 42 | role = "MEMBER" # OWNER/MANAGER/MEMBER 43 | } 44 | 45 | # Creating Simon Petlura user 46 | resource "gsuite_user" "simon_petlura" { 47 | # advise to set this field to true on creation, then false afterwards 48 | change_password_next_login = false 49 | name { 50 | family_name = "Petlura" 51 | given_name = "Simon" 52 | } 53 | # on creation this field is required 54 | # password = "rrjji944UI&83g*&^89tf" 55 | primary_email = "simon.petlura@company-test.com" 56 | } 57 | 58 | resource "gsuite_group_member" "simon_petlura" { 59 | group = "${gsuite_group.skunkworks-team.email}" 60 | email = "${gsuite_user.simon_petlura.primary_email}" 61 | role = "MEMBER" # OWNER/MANAGER/MEMBER 62 | } 63 | 64 | 65 | 66 | # --------------------------- 67 | # Creating GCP Project 68 | #---------------------------- 69 | 70 | 71 | provider "google" { 72 | region = "${var.region}" 73 | credentials = "${file("/Users/thor/.config/gcloud/infosec-terraform-admin.json")}" 74 | } 75 | 76 | resource "random_id" "id" { 77 | byte_length = 4 78 | prefix = "${var.skunkworks-project_name}-" 79 | } 80 | 81 | resource "google_project" "skunkworks-project" { 82 | name = "${var.skunkworks-project_name}" 83 | project_id = "${random_id.id.hex}" 84 | billing_account = "${var.billing_account}" 85 | org_id = "${var.org_id}" 86 | } 87 | 88 | resource "google_project_services" "skunkworks-project" { 89 | project = "${google_project.skunkworks-project.project_id}" 90 | services = [ 91 | "storage-api.googleapis.com" 92 | ] 93 | } 94 | 95 | resource "google_project_iam_binding" "skunkworks-project" { 96 | project = "${google_project.skunkworks-project.project_id}" 97 | role = "roles/editor" 98 | 99 | members = [ 100 | "group:${gsuite_group.skunkworks-team.email}", 101 | ] 102 | } 103 | 104 | 105 | 106 | 107 | 108 | output "project_id" { 109 | value = "${google_project.skunkworks-project.project_id}" 110 | } 111 | -------------------------------------------------------------------------------- /GCP/variables.tf: -------------------------------------------------------------------------------- 1 | variable "billing_account" { default= "111BBB-777DDD-354EEE" } 2 | variable "org_id" {default="1234567890"} 3 | variable "region" {default = "us-east1"} 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # it-security 2 | it-security related scripts and tools 3 | 4 | ## Folder: scripts.aws 5 | 6 | ### aws_test_bucket.py 7 | Auditing AWS account you have full access to is quite easy - just list the buckets and check theirs ACL, users and buckets' policies via aws cli or web gui. 8 | 9 | What about cases when you: 10 | * have many accounts and buckets (will take forever to audit manually) 11 | * do not have enough permissions in the target AWS account to check bucket access 12 | * you do not have permissions at all in this account (pentester mode) 13 | 14 | To address everything above I've created small tool to do all dirty job for you: 15 | 16 | 17 | ``` 18 | $python aws_test_bucket.py --profile prod-read --bucket test.bucket 19 | $python aws_test_bucket.py --profile prod-read --file aws 20 | $python aws_test_bucket.py --profile prod-read --file buckets.list 21 | 22 | -P AWS_PROFILE, --profile=AWS_PROFILE 23 | Please specify AWS CLI profile 24 | -B BUCKET, --bucket=BUCKET 25 | Please provide bucket name 26 | -F FILE, --file=FILE Optional: file with buckets list to check or aws to check all buckets in your account 27 | 28 | ``` 29 | **Note:** 30 | *--profile=AWS_PROFILE - yours AWS access profile (from aws cli). This profile might or might not have access to the audited bucket (we need this just to become Authenticated User from AWS point of view ).* 31 | 32 | If AWS_PROFILE allows authorized access to the bucket being audited - tool will fetch bucket's ACLs, Policies and S3 Static Web setting and perform authorized audit. 33 | 34 | If AWS_PROFILE does not allow authorized access - tool will work in pentester mode 35 | 36 | You can specify: 37 | * one bucket to check using **--bucket** option 38 | * file with list of buckets(one bucket name per line) using **--file** option 39 | * all buckets in your AWS account (accessible using AWS_PROFILE) using **--file=aws** option 40 | 41 | 42 | ---- 43 | 44 | 45 | ### s3_enc_check.py 46 | 47 | You have existing S3 bucket with data uploaded before you enable this policy, you have mixed (encrypted and non encrypted objects) or just doing security audit. In this case you need to scan the bucket to find unencrypted objects. How? quite easy using few python lines bellow: 48 | 49 | ``` 50 | import boto3 51 | import pprint 52 | import sys 53 | 54 | boto3.setup_default_session(profile_name='prod') 55 | s3 = boto3.resource('s3') 56 | if len(sys.argv) < 2: 57 | print "Missing bucket name" 58 | sys.exit 59 | bucket = s3.Bucket(sys.argv[1]) 60 | for obj in bucket.objects.all(): 61 | key = s3.Object(bucket.name, obj.key) 62 | if key.server_side_encryption is None: 63 | print "Not encrypted object found:", key 64 | ``` 65 | 66 | 67 | 68 | Nice, Yep, But it will take almost forever to scan bucket that contains thousand or tens of thousand of objects. In this it would be nice to have some counters, progress bar, ETA , summary, etc.. So, vuala: 69 | 70 | 71 | Small program providing all these features mentioned. Feel free to use it or request reasonable changes/modifications. 72 | 73 | --- 74 | 75 | ### aws_enforce_password_policy.py 76 | 77 | Small tool to check or/and enforce passsword policy on AWS account. Handy when you need to do this in many AWS accounts. 78 | 79 | ---- 80 | 81 | ### aws_secgroup_viewer .py 82 | Almost any AWS CloudFormation template are more then long enough. It's OK when you are dealing with different relatively "static" resources but become a big problem for something way more dynamic like security group. 83 | 84 | This kind of resource you need to modify and review a lot, especially if you cloud security professional. Reading AWS CloudFromation template JSON manually makes your life miserable and you can easily miss bunch of security problems and holes. 85 | 86 | My small aws_secgroup_viewer Python program helps you to quickly review and analyze all security groups in your template. 87 | 88 | Supports both security group notations used by CloudFormation: firewall rules inside security group or as separate resources linked to group. 89 | 90 | ---- 91 | 92 | ## Folders: tf and cf 93 | 94 | More details described in these blog posts 95 | 96 | **Updated** version with Terraform integration: http://blog.it-security.ca/2018/01/secure-your-aws-account-using-terrafrom.html 97 | 98 | Initial version: http://blog.it-security.ca/2016/11/secure-your-aws-account-using.html 99 | 100 | 101 | ### Secure your AWS account using Terraform and CloudFormation 102 | 103 | The very first thing you need to do while building your AWS infrastructure is to enable and configure all AWS account level security features such as: CloudTrail, CloudConfig, CloudWatch, IAM, etc.. 104 | To do this, you can use mine Amazon AWS Account level security checklist and how-to or any other source. 105 | To avoid manual steps and to be align with SecuityAsCode concept, I use Terraform and set of CloudFormation templates, simplified version of which described below. Now, it's 106 | 1. integrated with Terraform (use terraform templates in the folder **tf**) 107 | 2. creates prerequisites for Splunk integration (User, key, SNS, and SQS) 108 | 3. configures cross account access (for multiaccounts organizations, adding ITOrganizationAccountAccessRole with MFA enforced) 109 | 4. implements Section 3 (Monitoring) of the **CIS Amazon Web Services Foundations benchmark.** 110 | 5. configures CloudTrail according to the new best practices (KMS encryption, validation etc) 111 | 6. Configure basic set of the CloudConfig rules to monitor best practices 112 | 113 | 114 | #### Global Security stack template structure: 115 | 116 | **security.global.yaml** - parent template for all nested templates to link them together and control dependency between nested stacks. 117 | 118 | **cloudtrail.clobal.json** - nested template for Global configuration of the CloudTrail 119 | 120 | **cloudtrailalarms.global.json** - nested template for Global CloudWatch Logs alarms and security metrics creation. Uses FilterMap to create different security-related filters for ClouTrail LogGroup, corresponding metrics and notifications for suspicious or dangerous events. You can customise filter per environment basis. 121 | 122 | **awsconfig.global.json** - nested template for Global AWS Config Service configuration and config rules. 123 | 124 | **iam.global.json** - nested template for IAM Global configuration. 125 | 126 | 127 | #### Supported features: 128 | Environments and regions: Stack designed to cover all AWS account, but to be deployed in only one region. To deploy specify account nick-name and company name (these will be used to create unique s3 buckets names ) 129 | AWS services used by stack: CloudTrail, AWS Config, CloudWatch, CloudWatch Logs and Events, IAM. 130 | 131 | #### To deploy: 132 | 133 | 1. Get code from my git repo. 134 | 2. Update terraform.tfvars specifying: your AWS profile name (configured for aws cli using aws configure --profile profile_name); name for the environment (prod, test, dev ..) ; company(or division) name; region and AWS master account ID. 135 | 3. terraform init to get aws provider downloaded by terraform 136 | 4. terraform plan 137 | 5. terraform apply 138 | 139 | 140 | 141 | 142 | --- 143 | ## Folder: selfdefence.PoC.cf 144 | 145 | ### Self-Defending Cloud PoC or Amazon CloudWatch Events usage 146 | 147 | PoC of the Self-Defending Cloud concept described in this blog post: 148 | http://security-ingvar-ua.blogspot.ca/2016/10/self-defending-cloud-poc-or-amazon.html 149 | 150 | Files needed: 151 | selfdefence.infosec.vpc.json 152 | selfdefence_infosec.py 153 | 154 | To deploy you need: 155 | 1. selfdefence.infosec.vpc.json - template itself. 156 | 157 | 2. selfdefence_infosec.py - Lambda function. You will need to Zip it and upload to the s3 bucket with versioning enabled. 158 | 159 | 3. Edit template (selfdefence.infosec.vpc.json) and specify: S3 bucket name in format you.bucket.name.env.cloudform (where env - is your environment name: prod, test, staging, etc) and S3 version for selfdefence_infosec.zip file. 160 | 161 | 4. upload template to the same s3 bucket. 162 | 163 | 5. Create a stack using this template end specify corresponding environment name at the creation time. 164 | 165 | 166 | Enjoy! 167 | -------------------------------------------------------------------------------- /cf/security.global.cf/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Secure your AWS account using CloudFormation 3 | 4 | The very first thing you need to do while building your AWS infrastructure is to enable and configure all AWS account level security features such as: CloudTrail, CloudConfig, CloudWatch, IAM, etc.. 5 | To do this, you can use mine Amazon AWS Account level security checklist and how-to or any other source. 6 | To avoid manual steps and to be align with SecuityAsCode concept, I use set of CloudFormation templates, simplified version of which described below. Now, it's 7 | 1. integrated with Terraform (use terraform templates in the folder **tf**) 8 | 2. creates prerequisites for Splunk integration (User, key, SNS, and SQS) 9 | 3. configures cross account access (for multiaccounts organizations, adding ITOrganizationAccountAccessRole with MFA enforced) 10 | 4. implements Section 3 (Monitoring) of the **CIS Amazon Web Services Foundations benchmark.** 11 | 5. configures CloudTrail according to the new best practices (KMS encryption, validation etc) 12 | 6. Configure basic set of the CloudConfig rules to monitor best practices 13 | 14 | 15 | #### Global Security stack template structure: 16 | 17 | **security.global.yaml** - parent template for all nested templates to link them together and control dependency between nested stacks. 18 | 19 | **cloudtrail.clobal.json** - nested template for Global configuration of the CloudTrail 20 | 21 | **cloudtrailalarms.global.json** - nested template for Global CloudWatch Logs alarms and security metrics creation. Uses FilterMap to create different security-related filters for ClouTrail LogGroup, corresponding metrics and notifications for suspicious or dangerous events. You can customise filter per environment basis. 22 | 23 | **awsconfig.global.json** - nested template for Global AWS Config Service configuration and config rules. 24 | 25 | 26 | **iam.global.json** - nested template for IAM Global configuration. 27 | 28 | 29 | #### Supported features: 30 | Environments and regions: Stack designed to cover all AWS account, but to be deployed in only one region. To deploy specify account nick-name and company name (these will be used to create unique s3 buckets names ) 31 | AWS services used by stack: CloudTrail, AWS Config, CloudWatch, CloudWatch Logs and Events, IAM. 32 | -------------------------------------------------------------------------------- /cf/security.global.cf/awsconfig.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Enabling and configuring AWS Config service", 4 | "Parameters" : { 5 | "AWSConfigBucketName" : { 6 | "Description": "S3 Bucket name for the AWS Config Logs. Ex. com.ChangeMe.prod.infosec.awsconfig", 7 | "Type": "String", 8 | "MinLength" : "5", 9 | "MaxLength" : "100" 10 | } 11 | }, 12 | "Resources": { 13 | "s3infosecawsconfig": { 14 | "Type": "AWS::S3::Bucket", 15 | "Properties": { 16 | "AccessControl": "Private", 17 | "BucketName": {"Ref": "AWSConfigBucketName"}, 18 | "VersioningConfiguration": { 19 | "Status": "Suspended" 20 | } 21 | } 22 | }, 23 | "SQSAWSConfig": { 24 | "Type": "AWS::SQS::Queue", 25 | "Properties": { 26 | "QueueName" : "AWSConfigQueue", 27 | "MessageRetentionPeriod": "345600", 28 | "ReceiveMessageWaitTimeSeconds": "0" 29 | } 30 | }, 31 | "SQSAWSConfigPolicy" : { 32 | "Type" : "AWS::SQS::QueuePolicy", 33 | "DependsOn" : ["SQSAWSConfig", "AWSCloudConfigTopic"], 34 | "Properties" : { 35 | "PolicyDocument" : { 36 | "Id" : "SQSAWSConfigPolicy", 37 | "Version" : "2012-10-17", 38 | "Statement" : [ { 39 | "Sid" : "SQSAWSConfigPolicy2016", 40 | "Effect" : "Allow", 41 | "Principal" : { 42 | "AWS" : "*" 43 | }, 44 | "Action" : [ "sqs:SendMessage" ], 45 | "Resource" : {"Fn::GetAtt": ["SQSAWSConfig", "Arn"]}, 46 | "Condition" : { 47 | "ArnEquals": {"aws:SourceArn": { "Ref" : "AWSCloudConfigTopic" } } 48 | } 49 | } ] 50 | }, 51 | "Queues" : [ 52 | { "Ref" : "SQSAWSConfig" } 53 | ] 54 | } 55 | }, 56 | 57 | "AWSCloudConfigTopic": { 58 | "Type" : "AWS::SNS::Topic", 59 | "DependsOn" : "SQSAWSConfig", 60 | "Properties": { 61 | "TopicName" : "AWSCloudConfigTopic", 62 | "DisplayName" : "AWSCloudConfig", 63 | "Subscription": [ 64 | { 65 | "Endpoint": {"Fn::GetAtt": ["SQSAWSConfig", "Arn"]}, 66 | "Protocol": "sqs" 67 | } 68 | ], 69 | "TopicName" : "AWS-Config-Notification" 70 | } 71 | }, 72 | 73 | "AWSConfigPolicy": { 74 | "Type": "AWS::IAM::ManagedPolicy", 75 | "DependsOn": ["s3infosecawsconfig", "AWSCloudConfigTopic"], 76 | "Properties": { 77 | "ManagedPolicyName" : "AWSConfigPolicy", 78 | "Description" : "AWS Config Service policy", 79 | "Path" : "/infosec/policy/", 80 | "PolicyDocument" :{ 81 | "Version": "2012-10-17", 82 | "Statement": [ 83 | { "Effect": "Allow", 84 | "Action": [ "s3:PutObject" ], 85 | "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref": "AWSConfigBucketName"} , "/*" ]] }, 86 | "Condition": 87 | { 88 | "StringLike": 89 | { 90 | "s3:x-amz-acl": "bucket-owner-full-control" 91 | } 92 | } 93 | }, 94 | { 95 | "Effect": "Allow", 96 | "Action": [ "s3:GetBucketAcl" ], 97 | "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref": "AWSConfigBucketName"} ]] } 98 | }, 99 | { "Effect": "Allow", 100 | "Action": "sns:Publish", 101 | "Resource": { "Ref" : "AWSCloudConfigTopic" } 102 | } 103 | ] 104 | } 105 | } 106 | }, 107 | "AWSConfigRole": { 108 | "Type": "AWS::IAM::Role", 109 | "DependsOn": "AWSConfigPolicy", 110 | "Properties": { 111 | "RoleName" : "AWSConfigRole", 112 | "AssumeRolePolicyDocument": { 113 | "Version" : "2012-10-17", 114 | "Statement": [ { 115 | "Effect": "Allow", 116 | "Principal": { 117 | "Service": [ "config.amazonaws.com" ] 118 | }, 119 | "Action": [ "sts:AssumeRole" ] 120 | } ] 121 | }, 122 | "Path": "/infosec/services/", 123 | "ManagedPolicyArns" : [{"Ref": "AWSConfigPolicy"}, "arn:aws:iam::aws:policy/service-role/AWSConfigRole"] 124 | } 125 | }, 126 | "AWSConfigRecorder": { 127 | "Type": "AWS::Config::ConfigurationRecorder", 128 | "DependsOn" :["AWSConfigRole"], 129 | "Properties": { 130 | "Name": "AWSConfigRecorder", 131 | "RecordingGroup": { 132 | "AllSupported": "True", 133 | "IncludeGlobalResourceTypes" : "True" 134 | }, 135 | "RoleARN": {"Fn::GetAtt": ["AWSConfigRole", "Arn"]} 136 | } 137 | }, 138 | "AWSConfigDeliveryChannel": { 139 | "Type": "AWS::Config::DeliveryChannel", 140 | "DependsOn" : ["AWSCloudConfigTopic", "s3infosecawsconfig"], 141 | "Properties": { 142 | "Name" : "AWSConfigDeliveryChannel", 143 | "ConfigSnapshotDeliveryProperties": { 144 | "DeliveryFrequency": "One_Hour" 145 | }, 146 | "S3BucketName": {"Ref": "s3infosecawsconfig"}, 147 | "SnsTopicARN": {"Ref": "AWSCloudConfigTopic"} 148 | } 149 | }, 150 | 151 | "IAMUsersWithoutPolicy": { 152 | "Type": "AWS::Config::ConfigRule", 153 | "DependsOn" : "AWSConfigRecorder", 154 | "Properties": { 155 | "ConfigRuleName": "iam-user-no-policies-check", 156 | "Description": "Checks that none of your IAM users have policies attached. IAM users must inherit permissions from IAM groups or roles.", 157 | "InputParameters": {}, 158 | "Scope": { 159 | "ComplianceResourceTypes": [ 160 | "AWS::IAM::User" 161 | ] 162 | }, 163 | "Source": { 164 | "Owner": "AWS", 165 | "SourceIdentifier": "IAM_USER_NO_POLICIES_CHECK" 166 | } 167 | } 168 | }, 169 | 170 | "IAMRootMFA": { 171 | "Type": "AWS::Config::ConfigRule", 172 | "DependsOn" : "AWSConfigRecorder", 173 | "Properties": { 174 | "ConfigRuleName": "root-account-mfa-enabled", 175 | "Description": "Checks whether the root user of your AWS account requires multi-factor authentication for console sign-in.", 176 | "InputParameters": {}, 177 | "Scope": {}, 178 | "Source": { 179 | "Owner": "AWS", 180 | "SourceIdentifier": "ROOT_ACCOUNT_MFA_ENABLED" 181 | }, 182 | "MaximumExecutionFrequency": "One_Hour" 183 | } 184 | }, 185 | 186 | "S3PublicRead": { 187 | "Type": "AWS::Config::ConfigRule", 188 | "DependsOn" : "AWSConfigRecorder", 189 | "Properties": { 190 | "ConfigRuleName": "s3-bucket-public-read-prohibited", 191 | "Description": "Checks that your S3 buckets do not allow public read access. If an S3 bucket policy or bucket ACL allows public read access, the bucket is noncompliant.", 192 | "InputParameters": {}, 193 | "Scope": { 194 | "ComplianceResourceTypes": [ 195 | "AWS::S3::Bucket" 196 | ] 197 | }, 198 | "Source": { 199 | "Owner": "AWS", 200 | "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED" 201 | } 202 | } 203 | }, 204 | 205 | "S3PublicWrite": { 206 | "Type": "AWS::Config::ConfigRule", 207 | "DependsOn" : "AWSConfigRecorder", 208 | "Properties": { 209 | "ConfigRuleName": "s3-bucket-public-write-prohibited", 210 | "Description": "Checks that your S3 buckets do not allow public write access. If an S3 bucket policy or bucket ACL allows public write access, the bucket is noncompliant.", 211 | "InputParameters": {}, 212 | "Scope": { 213 | "ComplianceResourceTypes": [ 214 | "AWS::S3::Bucket" 215 | ] 216 | }, 217 | "Source": { 218 | "Owner": "AWS", 219 | "SourceIdentifier": "S3_BUCKET_PUBLIC_WRITE_PROHIBITED" 220 | } 221 | } 222 | }, 223 | 224 | "NetRestrictSSH": { 225 | "Type": "AWS::Config::ConfigRule", 226 | "DependsOn" : "AWSConfigRecorder", 227 | "Properties": { 228 | "ConfigRuleName": "restricted-ssh", 229 | "Description": "Checks whether security groups that are in use disallow unrestricted incoming SSH traffic.", 230 | "InputParameters": {}, 231 | "Scope": { 232 | "ComplianceResourceTypes": [ 233 | "AWS::EC2::SecurityGroup" 234 | ] 235 | }, 236 | "Source": { 237 | "Owner": "AWS", 238 | "SourceIdentifier": "INCOMING_SSH_DISABLED" 239 | } 240 | } 241 | }, 242 | "AccountPasswordPolicy": { 243 | "Type": "AWS::Config::ConfigRule", 244 | "DependsOn" : "AWSConfigRecorder", 245 | "Properties": { 246 | "ConfigRuleName": "iam-password-policy", 247 | "Description": "Checks whether the account password policy for IAM users meets the specified requirements.", 248 | "InputParameters": { 249 | "RequireUppercaseCharacters": "true", 250 | "RequireLowercaseCharacters": "true", 251 | "RequireSymbols": "true", 252 | "RequireNumbers": "true", 253 | "MinimumPasswordLength": "14", 254 | "PasswordReusePrevention": "24", 255 | "MaxPasswordAge": "90" 256 | }, 257 | "Scope": {}, 258 | "Source": { 259 | "Owner": "AWS", 260 | "SourceIdentifier": "IAM_PASSWORD_POLICY" 261 | }, 262 | "MaximumExecutionFrequency": "One_Hour" 263 | } 264 | } 265 | }, 266 | "Outputs" : { 267 | "SQSAWSConfigName" : { 268 | "Description": "SQS Queue Name for AWS Config", 269 | "Value" : {"Fn::GetAtt": ["SQSAWSConfig", "QueueName"]} 270 | }, 271 | "SQSAWSConfigArn" : { 272 | "Description": "SQS AWSConfigl ARN", 273 | "Value" : {"Fn::GetAtt": ["SQSAWSConfig", "Arn"]} 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /cf/security.global.cf/cloudtrail.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Enabling and configuring AWS CloudTrail", 4 | "Parameters" : { 5 | "cloudtrailBucketName" : { 6 | "Description": "S3 Bucket name for the CloudTrail Logs. Ex. com.ChangeMe.prod.infosec.cloudtrail", 7 | "Type": "String", 8 | "MinLength" : "5", 9 | "MaxLength" : "100" 10 | } 11 | }, 12 | "Resources": { 13 | "s3infosecawscloudtrail": { 14 | "Type": "AWS::S3::Bucket", 15 | "Properties": { 16 | "AccessControl": "Private", 17 | "BucketName": {"Ref": "cloudtrailBucketName"}, 18 | "VersioningConfiguration": { 19 | "Status": "Suspended" 20 | } 21 | } 22 | }, 23 | "s3policyrinfosecawscloudtrail": { 24 | "Type": "AWS::S3::BucketPolicy", 25 | "DependsOn": "s3infosecawscloudtrail", 26 | "Properties": { 27 | "Bucket": { 28 | "Ref": "s3infosecawscloudtrail" 29 | }, 30 | "PolicyDocument": { 31 | "Version": "2012-10-17", 32 | "Statement": [ 33 | { 34 | "Sid": "AWSCloudTrailAclCheck20150319", 35 | "Effect": "Allow", 36 | "Principal": { 37 | "Service": "cloudtrail.amazonaws.com" 38 | }, 39 | "Action": "s3:GetBucketAcl", 40 | "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref": "cloudtrailBucketName"} ]]} 41 | }, 42 | { 43 | "Sid": "AWSCloudTrailWrite20150319", 44 | "Effect": "Allow", 45 | "Principal": { 46 | "Service": "cloudtrail.amazonaws.com" 47 | }, 48 | "Action": "s3:PutObject", 49 | "Resource": { "Fn::Join" : ["", ["arn:aws:s3:::", {"Ref": "cloudtrailBucketName"} , "/*" ]]}, 50 | "Condition": { 51 | "StringEquals": { 52 | "s3:x-amz-acl": "bucket-owner-full-control" 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | } 59 | }, 60 | "CloudTrailEncKey" : { 61 | "Type" : "AWS::KMS::Key", 62 | "Properties" : { 63 | "Description" : "CloudTrail s3 Encryption Key", 64 | "KeyPolicy" : { 65 | "Version": "2012-10-17", 66 | "Id": "Key policy for CloudTrail", 67 | "Statement": [ 68 | { 69 | "Sid": "Enable IAM User Permissions", 70 | "Effect": "Allow", 71 | "Principal": {"AWS": [ 72 | { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":root" ]]} 73 | ]}, 74 | "Action": "kms:*", 75 | "Resource": "*" 76 | }, 77 | { 78 | "Sid": "Allow CloudTrail to encrypt logs", 79 | "Effect": "Allow", 80 | "Principal": {"Service": ["cloudtrail.amazonaws.com"]}, 81 | "Action": "kms:GenerateDataKey*", 82 | "Resource": "*", 83 | "Condition": {"StringLike": {"kms:EncryptionContext:aws:cloudtrail:arn": { "Fn::Join" : ["", ["arn:aws:cloudtrail:*:", {"Ref": "AWS::AccountId"}, ":trail/*" ]]}}} 84 | }, 85 | { 86 | "Sid": "Allow CloudTrail to describe key", 87 | "Effect": "Allow", 88 | "Principal": {"Service": ["cloudtrail.amazonaws.com"]}, 89 | "Action": "kms:DescribeKey", 90 | "Resource": "*" 91 | }, 92 | { 93 | "Sid": "Allow principals in the account to decrypt log files", 94 | "Effect": "Allow", 95 | "Principal": {"AWS": "*"}, 96 | "Action": [ 97 | "kms:Decrypt", 98 | "kms:ReEncryptFrom" 99 | ], 100 | "Resource": "*", 101 | "Condition": { 102 | "StringEquals": {"kms:CallerAccount": { "Ref" : "AWS::AccountId" }}, 103 | "StringLike": {"kms:EncryptionContext:aws:cloudtrail:arn": { "Fn::Join" : ["", ["arn:aws:cloudtrail:*:", {"Ref": "AWS::AccountId"}, ":trail/*" ]]}} 104 | } 105 | } 106 | ] 107 | } 108 | } 109 | }, 110 | "CloudTrailEncKeyAlias": { 111 | "Type" : "AWS::KMS::Alias", 112 | "DependsOn" : "CloudTrailEncKey", 113 | "Properties" : { 114 | "AliasName" : "alias/CloudTrailEncKey", 115 | "TargetKeyId" : { "Ref" : "CloudTrailEncKey" } 116 | } 117 | }, 118 | 119 | "SQSCloudTrail": { 120 | "Type": "AWS::SQS::Queue", 121 | "Properties": { 122 | "QueueName" : "CloudTrailQueue", 123 | "MessageRetentionPeriod": "345600", 124 | "ReceiveMessageWaitTimeSeconds": "0" 125 | } 126 | }, 127 | "CloudTrailTopic": { 128 | "Type" : "AWS::SNS::Topic", 129 | "DependsOn" : "SQSCloudTrail", 130 | "Properties": { 131 | "DisplayName" : "CloudTrail", 132 | "Subscription": [ 133 | { 134 | "Endpoint": {"Fn::GetAtt": ["SQSCloudTrail", "Arn"]}, 135 | "Protocol": "sqs" 136 | } 137 | ], 138 | "TopicName" : "CloudTrail-Notification" 139 | } 140 | }, 141 | "SQSCloudTrailPolicy" : { 142 | "Type" : "AWS::SQS::QueuePolicy", 143 | "DependsOn" : ["SQSCloudTrail", "CloudTrailTopic"], 144 | "Properties" : { 145 | "PolicyDocument" : { 146 | "Id" : "SQSCloudTrailPolicy", 147 | "Version" : "2012-10-17", 148 | "Statement" : [ { 149 | "Sid" : "SQSCloudTrailPolicy2016", 150 | "Effect" : "Allow", 151 | "Principal" : { 152 | "AWS" : "*" 153 | }, 154 | "Action" : [ "sqs:SendMessage" ], 155 | "Resource" : {"Fn::GetAtt": ["SQSCloudTrail", "Arn"]}, 156 | "Condition" : { 157 | "ArnEquals": {"aws:SourceArn": { "Ref" : "CloudTrailTopic" } } 158 | } 159 | } ] 160 | }, 161 | "Queues" : [ 162 | { "Ref" : "SQSCloudTrail" } 163 | ] 164 | } 165 | }, 166 | "CloudTrailTopicPolicy" : { 167 | "Type" : "AWS::SNS::TopicPolicy", 168 | "DependsOn" : "CloudTrailTopic", 169 | "Properties" : { 170 | "PolicyDocument" : { 171 | "Id" : "CloudTrailTopicPolicy", 172 | "Version" : "2012-10-17", 173 | "Statement" : [ { 174 | "Sid" : "AWSCloudTrailSNSPolicy20160512", 175 | "Effect" : "Allow", 176 | "Principal" : { 177 | "Service": "cloudtrail.amazonaws.com" 178 | }, 179 | "Action" : "sns:Publish", 180 | "Resource" : "*" 181 | } ] 182 | }, 183 | "Topics" : [ { "Ref" : "CloudTrailTopic" } ] 184 | } 185 | }, 186 | "CloudTrailLogRole": { 187 | "Type": "AWS::IAM::Role", 188 | "DependsOn": "CloudTrailLogGroup", 189 | "Properties": { 190 | "RoleName": "CloudTrailLogRole", 191 | "AssumeRolePolicyDocument": { 192 | "Version" : "2012-10-17", 193 | "Statement": [ { 194 | "Effect": "Allow", 195 | "Principal": { 196 | "Service": [ "cloudtrail.amazonaws.com" ] 197 | }, 198 | "Action": [ "sts:AssumeRole" ] 199 | } ] 200 | }, 201 | "Path": "/infosec/services/", 202 | "Policies" : [{ "PolicyName": "CloudTrailLog", "PolicyDocument": { 203 | "Version": "2012-10-17", 204 | "Statement": [ 205 | { 206 | "Sid": "AWSCloudTrailCreateLogStream2014110", 207 | "Effect": "Allow", 208 | "Action": [ 209 | "logs:CreateLogStream" 210 | ], 211 | "Resource": {"Fn::GetAtt":["CloudTrailLogGroup","Arn"]} 212 | 213 | }, 214 | { 215 | "Sid": "AWSCloudTrailPutLogEvents20141101", 216 | "Effect": "Allow", 217 | "Action": [ 218 | "logs:PutLogEvents" 219 | ], 220 | "Resource": {"Fn::GetAtt":["CloudTrailLogGroup","Arn"]} 221 | } 222 | ] 223 | }}] 224 | } 225 | }, 226 | "CloudTrailLogGroup": { 227 | "Type": "AWS::Logs::LogGroup", 228 | "Properties": { 229 | "LogGroupName": "GlobalCloudTrailLog" 230 | } 231 | }, 232 | "trailDefault": { 233 | "Type": "AWS::CloudTrail::Trail", 234 | "DependsOn" : ["CloudTrailLogRole", "CloudTrailLogGroup", "s3infosecawscloudtrail", "s3policyrinfosecawscloudtrail", "CloudTrailTopic", "CloudTrailTopicPolicy", "CloudTrailEncKey"], 235 | "Properties": { 236 | "TrailName" : "GlobalCloudTrail", 237 | "CloudWatchLogsLogGroupArn" : {"Fn::GetAtt":["CloudTrailLogGroup","Arn"]}, 238 | "CloudWatchLogsRoleArn" : {"Fn::GetAtt":["CloudTrailLogRole","Arn"]}, 239 | "IncludeGlobalServiceEvents": true, 240 | "EnableLogFileValidation": true, 241 | "IsMultiRegionTrail": true, 242 | "SnsTopicName" : {"Fn::GetAtt": ["CloudTrailTopic", "TopicName"]}, 243 | "IsLogging": "true", 244 | "KMSKeyId" : {"Ref": "CloudTrailEncKey"}, 245 | "S3KeyPrefix": "logs", 246 | "S3BucketName": { 247 | "Ref" : "s3infosecawscloudtrail" 248 | } 249 | } 250 | } 251 | }, 252 | "Outputs" : { 253 | "CloudWatchLogsLogGroup" : { 254 | "Description": "Cloudwatch Log Group ARN", 255 | "Value" : {"Ref": "CloudTrailLogGroup"} 256 | }, 257 | "SQSCloudTrailArn" : { 258 | "Description": "SQS CloudTrail ARN", 259 | "Value" : {"Fn::GetAtt": ["SQSCloudTrail", "Arn"]} 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /cf/security.global.cf/cloudtrailalarms.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | "Description" : "AWS CloudTrail API Activity Alarm Template for CloudWatch Logs", 4 | "Parameters" : { 5 | "LogGroupName" : { 6 | "Type" : "String", 7 | "Default" : "CloudTrail/DefaultLogGroup", 8 | "Description" : "Enter CloudWatch Logs log group name. Default is CloudTrail/DefaultLogGroup" 9 | }, 10 | "CompanyName" : { 11 | "Description": "CompanyName", 12 | "Type": "String", 13 | "MinLength" : "2", 14 | "MaxLength" : "100" 15 | }, 16 | "AccountNickname" : { 17 | "Description": "AWS Account nickname(purpose) to deploy", 18 | "Type": "String", 19 | "MinLength" : "2", 20 | "MaxLength" : "100" 21 | } 22 | }, 23 | "Mappings" : { 24 | "FilterMap" : { 25 | "31UnauthAPICalls" : {"all": "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\" ) }"}, 26 | "32MgmtConsoleNoMFA" : {"all": "{ ($.eventName = \"ConsoleLogin\") && ($.additionalEventData.MFAUsed!= \"Yes\") }"}, 27 | "33UseOfRootAcct" : { "all": "{ $.userIdentity.type = \"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != \"AwsServiceEvent\" }"}, 28 | "34IAMPolicyChanges" : { "all": "{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}"}, 29 | "35CloudTrailConfigChanges" : { "all": "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }"}, 30 | "36ConsoleAuthFailures" : {"all": "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failedauthentication\") }" }, 31 | "37DisableDeleteCMK" : {"all": "{($.eventSource = kms.amazonaws.com) && (($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion))}"}, 32 | "38S3BucketPolicyChanges" : {"all": "{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }"}, 33 | "39AWSConfigChanges" : {"all": "{($.eventSource = config.amazonaws.com) && (($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)||($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}"}, 34 | "310SecGroupChanges" : {"all": "{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup)}"}, 35 | "311NACLChanges" : { "all": "{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }"}, 36 | "312NetworkGatewayChanges" : { "all": "{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }"}, 37 | "313RouteTableChanges" : { "all": "{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }"}, 38 | "314VPCChanges" : { "all": "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }"}, 39 | 40 | "vpc-flow-logs" : { "all": "{$.eventName = CreateFlowLogs || $.eventName = DeleteFlowLogs}"}, 41 | "detach-force-ebs" : { "all": "{$.eventName = DetachVolume && $.requestParameters.force IS TRUE}"}, 42 | "massive-operations" : { "all": "{($.eventName = StopInstances || $.eventName = TerminateInstances || $.eventName = RebootInstances || $.eventName = RunInstances || $.eventName = StartInstances)}"}, 43 | "massive-terminations" : { "all": "{$.eventName = TerminateInstances}"}, 44 | 45 | 46 | "rds-change" : { "all": "{$.eventName = CopyDB* || $.eventName = CreateDB* || $.eventName = DeleteDB*}"}, 47 | "srt-instance" : { "all": "{($.eventName = StopInstances || $.eventName = TerminateInstances || $.eventName = RebootInstances)}"}, 48 | "large-instance" : { "all": "{ (($.eventName = RunInstances) || ($.eventName = StartInstances)) && (($.requestParameters.instanceType = *.2xlarge) || ($.requestParameters.instanceType = *.4xlarge) || ($.requestParameters.instanceType = *.8xlarge) || ($.requestParameters.instanceType = *.10xlarge)) }"}, 49 | "change-critical-ebs" : { "prod": "{($.eventName = DetachVolume || $.eventName = AttachVolume || $.eventName = CreateVolume || $.eventName = DeleteVolume || $.eventName = EnableVolumeIO || $.eventName = ImportVolume || $.eventName = ModifyVolumeAttribute) && ($.requestParameters.volumeId = vol-youvol1ID || $.requestParameters.volumeId = vol-youvol2ID)}"}, 50 | "create-delete-secgroup" : { "all": "{$.eventName = CreateSecurityGroup || $.eventName = CreateCacheSecurityGroup || $.eventName = CreateClusterSecurityGroup || $.eventName = CreateDBSecurityGroup || $.eventName = DeleteSecurityGroup || $.eventName = DeleteCacheSecurityGroup || $.eventName = DeleteClusterSecurityGroup || $.eventName = DeleteDBSecurityGroup}"}, 51 | "secgroup-instance" : { "all": "{$.eventName = ModifyInstanceAttribute && $.requestParameters.groupSet.items[0].groupId = * }"}, 52 | "cloudformation-change" : { "all": "{$.eventSource = cloudformation.amazonaws.com && ($.eventName != Validate* && $.eventName != Describe* && $.eventName != List* && $.eventName != Get*)}"}, 53 | "critical-instance" : { "prod": "{$.requestParameters.instanceId = i-instance1ID || $.requestParameters.instanceId = i-instance2ID || $.requestParameters.instanceId = i-instance3ID || $.requestParameters.instanceId = i-instance4ID || $.requestParameters.instanceId = i-instance5ID || $.requestParameters.instanceId = i-instance6ID|| $.requestParameters.instanceId = i-instance7ID}"}, 54 | "eip-change" : { "all": "{$.eventName = AssociateAddress || $.eventName = DisassociateAddress || $.eventName = MoveAddressToVpc || $.eventName = ReleaseAddress }"}, 55 | "net-access" : { "all": "{$.sourceIPAddress != 111.222.3* && $.sourceIPAddress != 111.222.4* && $.sourceIPAddress != cloud* && $.sourceIPAddress != AWS* && $.sourceIPAddress != 11.22.33.00 && $.sourceIPAddress != 11.22.33.01 }"}, 56 | "test-change" : { "staging": "value", "prod" : "value", "dev": "value" } 57 | } 58 | }, 59 | "Resources" : { 60 | "31UnauthAPICallsMetricFilter": { 61 | "Type": "AWS::Logs::MetricFilter", 62 | "Properties": { 63 | "LogGroupName": { "Ref": "LogGroupName"}, 64 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "31UnauthAPICalls", "all"]}, 65 | "MetricTransformations": [ 66 | { 67 | "MetricNamespace": "CloudTrailMetrics", 68 | "MetricName": "31UnauthAPICallsEventCount", 69 | "MetricValue": "1" 70 | } 71 | ] 72 | } 73 | }, 74 | "31UnauthAPICallsAlarm": { 75 | "Type": "AWS::CloudWatch::Alarm", 76 | "Properties": { 77 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "31UnauthAPICalls"]]}, 78 | "AlarmDescription" : "3.01 Ensure a log metric filter and alarm exist for unauthorized API calls", 79 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}], 80 | "MetricName" : "31UnauthAPICallsEventCount", 81 | "Namespace" : "CloudTrailMetrics", 82 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 83 | "EvaluationPeriods" : "1", 84 | "Period" : "300", 85 | "Statistic" : "Sum", 86 | "Threshold" : "1" 87 | } 88 | }, 89 | 90 | "32MgmtConsoleNoMFAMetricFilter": { 91 | "Type": "AWS::Logs::MetricFilter", 92 | "Properties": { 93 | "LogGroupName": { "Ref": "LogGroupName"}, 94 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "32MgmtConsoleNoMFA", "all"]}, 95 | "MetricTransformations": [ 96 | { 97 | "MetricNamespace": "CloudTrailMetrics", 98 | "MetricName": "32MgmtConsoleNoMFAEventCount", 99 | "MetricValue": "1" 100 | } 101 | ] 102 | } 103 | }, 104 | "32MgmtConsoleNoMFAAlarm": { 105 | "Type": "AWS::CloudWatch::Alarm", 106 | "Properties": { 107 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "32MgmtConsoleNoMFA"]]}, 108 | "AlarmDescription" : "3.02 Ensure a log metric filter and alarm exist for Management Console sign-in without MFA ", 109 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}], 110 | "MetricName" : "32MgmtConsoleNoMFAEventCount", 111 | "Namespace" : "CloudTrailMetrics", 112 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 113 | "EvaluationPeriods" : "1", 114 | "Period" : "300", 115 | "Statistic" : "Sum", 116 | "Threshold" : "1" 117 | } 118 | }, 119 | "33UseOfRootAcctMetricFilter": { 120 | "Type": "AWS::Logs::MetricFilter", 121 | "Properties": { 122 | "LogGroupName": { "Ref": "LogGroupName"}, 123 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "33UseOfRootAcct", "all"]}, 124 | "MetricTransformations": [ 125 | { 126 | "MetricNamespace": "CloudTrailMetrics", 127 | "MetricName": "33UseOfRootAcctEventCount", 128 | "MetricValue": "1" 129 | } 130 | ] 131 | } 132 | }, 133 | "33UseOfRootAcctAlarm": { 134 | "Type": "AWS::CloudWatch::Alarm", 135 | "Properties": { 136 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "33UseOfRootAcct"]]}, 137 | "AlarmDescription" : "3.3 Ensure a log metric filter and alarm exist for usage of Root account", 138 | "AlarmActions" : [{ "Ref" : "InfosecSMSTopic" }, { "Ref" : "InfosecEmailTopic" }], 139 | "MetricName" : "33UseOfRootAcctEventCount", 140 | "Namespace" : "CloudTrailMetrics", 141 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 142 | "EvaluationPeriods" : "1", 143 | "Period" : "300", 144 | "Statistic" : "Sum", 145 | "Threshold" : "1" 146 | } 147 | }, 148 | "34IAMPolicyChangesMetricFilter": { 149 | "Type": "AWS::Logs::MetricFilter", 150 | "Properties": { 151 | "LogGroupName": { "Ref": "LogGroupName"}, 152 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "34IAMPolicyChanges", "all"]}, 153 | "MetricTransformations": [ 154 | { 155 | "MetricNamespace": "CloudTrailMetrics", 156 | "MetricName": "34IAMPolicyChangesEventCount", 157 | "MetricValue": "1" 158 | } 159 | ] 160 | } 161 | }, 162 | "34IAMPolicyChangesAlarm": { 163 | "Type": "AWS::CloudWatch::Alarm", 164 | "Properties": { 165 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} ,"34IAMPolicyChanges"]]}, 166 | "AlarmDescription" : "3.4 Ensure a log metric filter and alarm exist for IAM policy changes", 167 | "AlarmActions" : [{ "Ref" : "InfosecEmailTopic" }], 168 | "MetricName" : "34IAMPolicyChangesEventCount", 169 | "Namespace" : "CloudTrailMetrics", 170 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 171 | "EvaluationPeriods" : "1", 172 | "Period" : "300", 173 | "Statistic" : "Sum", 174 | "Threshold" : "1" 175 | } 176 | }, 177 | "35CloudTrailConfigChangesMetricFilter": { 178 | "Type": "AWS::Logs::MetricFilter", 179 | "Properties": { 180 | "LogGroupName": { "Ref": "LogGroupName"}, 181 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "35CloudTrailConfigChanges", "all"]}, 182 | "MetricTransformations": [ 183 | { 184 | "MetricNamespace": "CloudTrailMetrics", 185 | "MetricName": "35CloudTrailConfigChangesEventCount", 186 | "MetricValue": "1" 187 | } 188 | ] 189 | } 190 | }, 191 | "35CloudTrailConfigChangesAlarm": { 192 | "Type": "AWS::CloudWatch::Alarm", 193 | "Properties": { 194 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "35CloudTrailConfigChanges"]]}, 195 | "AlarmDescription" : "3.5 Ensure a log metric filter and alarm exist for CloudTrail configuration changes", 196 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 197 | "MetricName" : "35CloudTrailConfigChangesEventCount", 198 | "Namespace" : "CloudTrailMetrics", 199 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 200 | "EvaluationPeriods" : "1", 201 | "Period" : "300", 202 | "Statistic" : "Sum", 203 | "Threshold" : "1" 204 | } 205 | }, 206 | "36ConsoleAuthFailuresMetricFilter": { 207 | "Type": "AWS::Logs::MetricFilter", 208 | "Properties": { 209 | "LogGroupName": { "Ref": "LogGroupName"}, 210 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "36ConsoleAuthFailures", "all"]}, 211 | "MetricTransformations": [ 212 | { 213 | "MetricNamespace": "CloudTrailMetrics", 214 | "MetricName": "36ConsoleAuthFailuresEventCount", 215 | "MetricValue": "1" 216 | } 217 | ] 218 | } 219 | }, 220 | "36ConsoleAuthFailuresAlarm": { 221 | "Type": "AWS::CloudWatch::Alarm", 222 | "Properties": { 223 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "36ConsoleAuthFailures"]]}, 224 | "AlarmDescription" : "3.6 Ensure a log metric filter and alarm exist for AWS Management Console authentication failures", 225 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 226 | "MetricName" : "36ConsoleAuthFailuresEventCount", 227 | "Namespace" : "CloudTrailMetrics", 228 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 229 | "EvaluationPeriods" : "1", 230 | "Period" : "300", 231 | "Statistic" : "Sum", 232 | "Threshold" : "1" 233 | } 234 | }, 235 | "37DisableDeleteCMKMetricFilter": { 236 | "Type": "AWS::Logs::MetricFilter", 237 | "Properties": { 238 | "LogGroupName": { "Ref": "LogGroupName"}, 239 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "37DisableDeleteCMK", "all"]}, 240 | "MetricTransformations": [ 241 | { 242 | "MetricNamespace": "CloudTrailMetrics", 243 | "MetricName": "37DisableDeleteCMKEventCount", 244 | "MetricValue": "1" 245 | } 246 | ] 247 | } 248 | }, 249 | "37DisableDeleteCMKAlarm": { 250 | "Type": "AWS::CloudWatch::Alarm", 251 | "Properties": { 252 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "37DisableDeleteCMK"]]}, 253 | "AlarmDescription" : "3.7 Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs ", 254 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 255 | "MetricName" : "37DisableDeleteCMKEventCount", 256 | "Namespace" : "CloudTrailMetrics", 257 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 258 | "EvaluationPeriods" : "1", 259 | "Period" : "300", 260 | "Statistic" : "Sum", 261 | "Threshold" : "1" 262 | } 263 | }, 264 | "38S3BucketPolicyChangesMetricFilter": { 265 | "Type": "AWS::Logs::MetricFilter", 266 | "Properties": { 267 | "LogGroupName": { "Ref": "LogGroupName"}, 268 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "38S3BucketPolicyChanges", "all"]}, 269 | "MetricTransformations": [ 270 | { 271 | "MetricNamespace": "CloudTrailMetrics", 272 | "MetricName": "38S3BucketPolicyChangesEventCount", 273 | "MetricValue": "1" 274 | } 275 | ] 276 | } 277 | }, 278 | "338S3BucketPolicyChangesAlarm": { 279 | "Type": "AWS::CloudWatch::Alarm", 280 | "Properties": { 281 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "38S3BucketPolicyChanges"]]}, 282 | "AlarmDescription" : "3.8 Ensure a log metric filter and alarm exist for S3 bucket policy changes ", 283 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 284 | "MetricName" : "38S3BucketPolicyChangesEventCount", 285 | "Namespace" : "CloudTrailMetrics", 286 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 287 | "EvaluationPeriods" : "1", 288 | "Period" : "300", 289 | "Statistic" : "Sum", 290 | "Threshold" : "1" 291 | } 292 | }, 293 | "39AWSConfigChangesMetricFilter": { 294 | "Type": "AWS::Logs::MetricFilter", 295 | "Properties": { 296 | "LogGroupName": { "Ref": "LogGroupName"}, 297 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "39AWSConfigChanges", "all"]}, 298 | "MetricTransformations": [ 299 | { 300 | "MetricNamespace": "CloudTrailMetrics", 301 | "MetricName": "39AWSConfigChangesEventCount", 302 | "MetricValue": "1" 303 | } 304 | ] 305 | } 306 | }, 307 | "39AWSConfigChangesAlarm": { 308 | "Type": "AWS::CloudWatch::Alarm", 309 | "Properties": { 310 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "39AWSConfigChanges"]]}, 311 | "AlarmDescription" : "3.9 Ensure a log metric filter and alarm exist for AWS Config configuration changes", 312 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 313 | "MetricName" : "39AWSConfigChangesEventCount", 314 | "Namespace" : "CloudTrailMetrics", 315 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 316 | "EvaluationPeriods" : "1", 317 | "Period" : "300", 318 | "Statistic" : "Sum", 319 | "Threshold" : "1" 320 | } 321 | }, 322 | "310SecGroupChangesMetricFilter": { 323 | "Type": "AWS::Logs::MetricFilter", 324 | "Properties": { 325 | "LogGroupName": { "Ref": "LogGroupName"}, 326 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "310SecGroupChanges", "all"]}, 327 | "MetricTransformations": [ 328 | { 329 | "MetricNamespace": "CloudTrailMetrics", 330 | "MetricName": "310SecGroupChangesEventCount", 331 | "MetricValue": "1" 332 | } 333 | ] 334 | } 335 | }, 336 | "310SecGroupChangesAlarm": { 337 | "Type": "AWS::CloudWatch::Alarm", 338 | "Properties": { 339 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} ,"310SecGroupChanges"]]}, 340 | "AlarmDescription" : "3.10 Ensure a log metric filter and alarm exist for security group changes", 341 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}], 342 | "MetricName" : "310SecGroupChangesEventCount", 343 | "Namespace" : "CloudTrailMetrics", 344 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 345 | "EvaluationPeriods" : "1", 346 | "Period" : "300", 347 | "Statistic" : "Sum", 348 | "Threshold" : "1" 349 | } 350 | }, 351 | "311NACLChangesMetricFilter": { 352 | "Type": "AWS::Logs::MetricFilter", 353 | "Properties": { 354 | "LogGroupName": { "Ref": "LogGroupName"}, 355 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "311NACLChanges", "all"]}, 356 | "MetricTransformations": [ 357 | { 358 | "MetricNamespace": "CloudTrailMetrics", 359 | "MetricName": "311NACLChangesEventCount", 360 | "MetricValue": "1" 361 | } 362 | ] 363 | } 364 | }, 365 | "311NACLChangesAlarm": { 366 | "Type": "AWS::CloudWatch::Alarm", 367 | "Properties": { 368 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "311NACLChanges"]]}, 369 | "AlarmDescription" : "3.11 Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) ", 370 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}, {"Ref": "DevOpsEmailTopic"}], 371 | "MetricName" : "311NACLChangesEventCount", 372 | "Namespace" : "CloudTrailMetrics", 373 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 374 | "EvaluationPeriods" : "1", 375 | "Period" : "300", 376 | "Statistic" : "Sum", 377 | "Threshold" : "1" 378 | } 379 | }, 380 | "312NetworkGatewayChangesMetricFilter": { 381 | "Type": "AWS::Logs::MetricFilter", 382 | "Properties": { 383 | "LogGroupName": { "Ref": "LogGroupName"}, 384 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "312NetworkGatewayChanges", "all"]}, 385 | "MetricTransformations": [ 386 | { 387 | "MetricNamespace": "CloudTrailMetrics", 388 | "MetricName": "312NetworkGatewayChangesEventCount", 389 | "MetricValue": "1" 390 | } 391 | ] 392 | } 393 | }, 394 | "312NetworkGatewayChangesAlarm": { 395 | "Type": "AWS::CloudWatch::Alarm", 396 | "Properties": { 397 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "312NetworkGatewayChanges"]]}, 398 | "AlarmDescription" : "3.12 Ensure a log metric filter and alarm exist for changes to network gateways", 399 | "AlarmActions" : [{"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }, {"Ref": "DevOpsSMSTopic"}], 400 | "MetricName" : "312NetworkGatewayChangesEventCount", 401 | "Namespace" : "CloudTrailMetrics", 402 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 403 | "EvaluationPeriods" : "1", 404 | "Period" : "300", 405 | "Statistic" : "Sum", 406 | "Threshold" : "1" 407 | } 408 | }, 409 | "313RouteTableChangesMetricFilter": { 410 | "Type": "AWS::Logs::MetricFilter", 411 | "Properties": { 412 | "LogGroupName": { "Ref": "LogGroupName"}, 413 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "313RouteTableChanges", "all"]}, 414 | "MetricTransformations": [ 415 | { 416 | "MetricNamespace": "CloudTrailMetrics", 417 | "MetricName": "313RouteTableChangesEventCount", 418 | "MetricValue": "1" 419 | } 420 | ] 421 | } 422 | }, 423 | "313RouteTableChangesAlarm": { 424 | "Type": "AWS::CloudWatch::Alarm", 425 | "Properties": { 426 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "313RouteTableChanges"]]}, 427 | "AlarmDescription" : "3.13 Ensure a log metric filter and alarm exist for route table changes ", 428 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}, {"Ref": "DevOpsEmailTopic"}], 429 | "MetricName" : "313RouteTableChangesEventCount", 430 | "Namespace" : "CloudTrailMetrics", 431 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 432 | "EvaluationPeriods" : "1", 433 | "Period" : "300", 434 | "Statistic" : "Sum", 435 | "Threshold" : "1" 436 | } 437 | }, 438 | "314VPCChangesMetricFilter": { 439 | "Type": "AWS::Logs::MetricFilter", 440 | "Properties": { 441 | "LogGroupName": { "Ref": "LogGroupName"}, 442 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "314VPCChanges", "all"]}, 443 | "MetricTransformations": [ 444 | { 445 | "MetricNamespace": "CloudTrailMetrics", 446 | "MetricName": "314VPCChangesEventCount", 447 | "MetricValue": "1" 448 | } 449 | ] 450 | } 451 | }, 452 | "314VPCChangesAlarm": { 453 | "Type": "AWS::CloudWatch::Alarm", 454 | "Properties": { 455 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "314VPCChanges"]]}, 456 | "AlarmDescription" : "3.14 Ensure a log metric filter and alarm exist for VPC changes", 457 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}, {"Ref": "DevOpsEmailTopic"}], 458 | "MetricName" : "314VPCChangesEventCount", 459 | "Namespace" : "CloudTrailMetrics", 460 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 461 | "EvaluationPeriods" : "1", 462 | "Period" : "300", 463 | "Statistic" : "Sum", 464 | "Threshold" : "1" 465 | } 466 | }, 467 | 468 | 469 | 470 | 471 | "MassiveOptMetricFilter": { 472 | "Type": "AWS::Logs::MetricFilter", 473 | "Properties": { 474 | "LogGroupName": { "Ref": "LogGroupName"}, 475 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "massive-operations", "all"]}, 476 | "MetricTransformations": [ 477 | { 478 | "MetricNamespace": "CloudTrailMetrics", 479 | "MetricName": "MassiveOptEventCount", 480 | "MetricValue": "1" 481 | } 482 | ] 483 | } 484 | }, 485 | "MassiveOptAlarm": { 486 | "Type": "AWS::CloudWatch::Alarm", 487 | "Properties": { 488 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} ,"MassiveOperations"]]}, 489 | "AlarmDescription" : "Alarms when a large number of operations are performed in small time period", 490 | "AlarmActions" : [ {"Ref": "DevOpsSMSTopic"}, {"Ref": "InfosecSMSTopic"}, { "Ref" : "InfosecEmailTopic" }], 491 | "MetricName" : "MassiveOptEventCount", 492 | "Namespace" : "CloudTrailMetrics", 493 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 494 | "EvaluationPeriods" : "1", 495 | "Period" : "300", 496 | "Statistic" : "Sum", 497 | "Threshold" : "10" 498 | } 499 | }, 500 | "MassiveTerminationMetricFilter": { 501 | "Type": "AWS::Logs::MetricFilter", 502 | "Properties": { 503 | "LogGroupName": { "Ref": "LogGroupName"}, 504 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "massive-terminations", "all"]}, 505 | "MetricTransformations": [ 506 | { 507 | "MetricNamespace": "CloudTrailMetrics", 508 | "MetricName": "MassiveTerminationEventCount", 509 | "MetricValue": "1" 510 | } 511 | ] 512 | } 513 | }, 514 | "MassiveTerminationAlarm": { 515 | "Type": "AWS::CloudWatch::Alarm", 516 | "Properties": { 517 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} ,"MassiveTermination"]]}, 518 | "AlarmDescription" : "Alarms when a large number of Instances are being terminated ", 519 | "AlarmActions" : [{"Ref": "InfosecSMSTopic" }, { "Ref" : "InfosecEmailTopic" }, {"Ref": "DevOpsSMSTopic"}], 520 | "MetricName" : "MassiveTerminationEventCount", 521 | "Namespace" : "CloudTrailMetrics", 522 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 523 | "EvaluationPeriods" : "1", 524 | "Period" : "60", 525 | "Statistic" : "Sum", 526 | "Threshold" : "10" 527 | } 528 | }, 529 | 530 | "EBSForceDetachMetricFilter": { 531 | "Type": "AWS::Logs::MetricFilter", 532 | "Properties": { 533 | "LogGroupName": { "Ref": "LogGroupName"}, 534 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "detach-force-ebs", "all"]}, 535 | "MetricTransformations": [ 536 | { 537 | "MetricNamespace": "CloudTrailMetrics", 538 | "MetricName": "VolumeForceDetachEventCount", 539 | "MetricValue": "1" 540 | } 541 | ] 542 | } 543 | }, 544 | "EBSForceDetachAlarm": { 545 | "Type": "AWS::CloudWatch::Alarm", 546 | "Properties": { 547 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} ,"EBSForceDetach"]]}, 548 | "AlarmDescription" : "Alarms when a volume is force detached from an Instance", 549 | "AlarmActions" : [{"Ref": "DevOpsEmailTopic" }], 550 | "MetricName" : "VolumeForceDetachEventCount", 551 | "Namespace" : "CloudTrailMetrics", 552 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 553 | "EvaluationPeriods" : "1", 554 | "Period" : "300", 555 | "Statistic" : "Sum", 556 | "Threshold" : "1" 557 | } 558 | }, 559 | 560 | "VPCTrafficFlowCreateDelMetricFilter": { 561 | "Type": "AWS::Logs::MetricFilter", 562 | "Properties": { 563 | "LogGroupName": { "Ref": "LogGroupName"}, 564 | "FilterPattern": { "Fn::FindInMap" : [ "FilterMap", "vpc-flow-logs", "all"]}, 565 | "MetricTransformations": [ 566 | { 567 | "MetricNamespace": "CloudTrailMetrics", 568 | "MetricName": "VPCTrafficFlowEventCount", 569 | "MetricValue": "1" 570 | } 571 | ] 572 | } 573 | }, 574 | "VPCTrafficFlowCreateDelAlarm": { 575 | "Type": "AWS::CloudWatch::Alarm", 576 | "Properties": { 577 | "AlarmName" : { "Fn::Join" : ["--", [ {"Ref": "CompanyName"} , {"Ref": "AccountNickname"} , "VPCTrafficFlowCreateDel"]]}, 578 | "AlarmDescription" : "Alarms when VPC traffic flow is created or deleted", 579 | "AlarmActions" : [{"Ref": "InfosecEmailTopic"}], 580 | "MetricName" : "VPCTrafficFlowEventCount", 581 | "Namespace" : "CloudTrailMetrics", 582 | "ComparisonOperator" : "GreaterThanOrEqualToThreshold", 583 | "EvaluationPeriods" : "1", 584 | "Period" : "300", 585 | "Statistic" : "Sum", 586 | "Threshold" : "1" 587 | } 588 | }, 589 | 590 | 591 | "InfosecEmailTopic": { 592 | "Type" : "AWS::SNS::Topic", 593 | "Properties": { 594 | "DisplayName" : "Infosec-Email", 595 | "Subscription": [ 596 | { 597 | "Endpoint": "infosec.notifications+AWS@it-security.ca", 598 | "Protocol": "email" 599 | } 600 | ], 601 | "TopicName" : "Infosec-Email-Notification" 602 | } 603 | }, 604 | "DevOpsEmailTopic": { 605 | "Type" : "AWS::SNS::Topic", 606 | "Properties": { 607 | "DisplayName" : "DevOps-Email", 608 | "Subscription": [ 609 | { 610 | "Endpoint": "infosec.notifications+AWS@it-security.ca", 611 | "Protocol": "email" 612 | } 613 | ], 614 | "TopicName" : "DevOps-Email-Notification" 615 | } 616 | }, 617 | "InfosecSMSTopic": { 618 | "Type" : "AWS::SNS::Topic", 619 | "Properties": { 620 | "DisplayName" : "Infosec-SMS", 621 | "TopicName" : "Infosec-SMS-Critical" 622 | } 623 | }, 624 | "DevOpsSMSTopic": { 625 | "Type" : "AWS::SNS::Topic", 626 | "Properties": { 627 | "DisplayName" : "DevOps-SMS", 628 | "TopicName" : "DevOps-SMS-Critical" 629 | } 630 | } 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /cf/security.global.cf/iam.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Creating Groups and Security Policies for IAM", 4 | "Parameters" : { 5 | "AccountNickname" : { 6 | "Description": "AWS Account nickname(purpose) to deploy", 7 | "Type": "String", 8 | "MinLength" : "2", 9 | "MaxLength" : "100" 10 | }, 11 | "SQSCloudTrailArn" : { 12 | "Description": "SQS Queue for CloudTrail events Arn", 13 | "Type": "String", 14 | "MinLength" : "2", 15 | "MaxLength" : "500" 16 | }, 17 | "SQSAWSConfigArn" : { 18 | "Description": "SQS Queue for AWSConfig events Arn", 19 | "Type": "String", 20 | "MinLength" : "2", 21 | "MaxLength" : "500" 22 | }, 23 | "MasterAccount" : { 24 | "Description": "AWS Organization Master Account", 25 | "Type": "String", 26 | "MinLength" : "2", 27 | "MaxLength" : "100" 28 | } 29 | }, 30 | 31 | "Mappings" : { 32 | "Enviroments" : { 33 | "regions" : { "staging": "us-east-1:", "prod" : "us-east-1:", "dev": "us-east-1:", "dr": "us-east-1:" } 34 | } 35 | }, 36 | "Resources": { 37 | "UserSelfServicePolicy": { 38 | "Type": "AWS::IAM::ManagedPolicy", 39 | "Properties": { 40 | "ManagedPolicyName" : "UserSelfServicePolicy", 41 | "Description" : "Self ManagedPolicy for all users", 42 | "Path" : "/infosec/policy/", 43 | "PolicyDocument" :{ 44 | "Version": "2012-10-17", 45 | "Statement": [ 46 | { 47 | "Sid": "AllowUsersToCreateEnableResyncTheirOwnVirtualMFADeviceandChangePassword", 48 | "Effect": "Allow", 49 | "Action": [ 50 | "iam:CreateVirtualMFADevice", 51 | "iam:EnableMFADevice", 52 | "iam:ResyncMFADevice", 53 | "iam:*AccessKey*", 54 | "iam:*SSHPublicKey*", 55 | "iam:ChangePassword" 56 | ], 57 | "Resource": [ 58 | { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "AWS::AccountId"} , ":mfa/${aws:username}" ]]}, 59 | { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "AWS::AccountId"} , ":user/${aws:username}" ]]} 60 | ] 61 | }, 62 | { 63 | "Sid": "AllowUsersToDeactivateDeleteTheirOwnVirtualMFADevice", 64 | "Effect": "Allow", 65 | "Action": [ 66 | "iam:DeactivateMFADevice", 67 | "iam:DeleteVirtualMFADevice" 68 | ], 69 | "Resource": [ 70 | { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "AWS::AccountId"} , ":mfa/${aws:username}" ]]}, 71 | { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "AWS::AccountId"} , ":user/${aws:username}" ]]} 72 | ], 73 | "Condition": { 74 | "Bool": { 75 | "aws:MultiFactorAuthPresent": "true" 76 | } 77 | } 78 | }, 79 | { 80 | "Sid": "AllowUsersToListMFADevicesandUsersandPasswordPolicyForConsole", 81 | "Effect": "Allow", 82 | "Action": [ 83 | "iam:ListMFADevices", 84 | "iam:ListVirtualMFADevices", 85 | "iam:ListUsers", 86 | "iam:GetAccountPasswordPolicy" 87 | ], 88 | "Resource": "*" 89 | } 90 | ] 91 | } 92 | } 93 | }, 94 | 95 | "ProtectProdEnviroment": { 96 | "Type": "AWS::IAM::ManagedPolicy", 97 | "Properties": { 98 | "ManagedPolicyName" : "LimitDistructiveActions", 99 | "Description" : "Block some modify/delete actions on core production components/functions.", 100 | "Path" : "/infosec/policy/", 101 | "PolicyDocument" :{ 102 | "Version": "2012-10-17", 103 | "Statement": [ 104 | { 105 | "Sid": "EnforceInfrastructureProtection20160801", 106 | "Effect": "Deny", 107 | "Action": [ 108 | "ec2:DeleteVpc*", 109 | "ec2:ModifyVpc*", 110 | "ec2:DeleteFlowLogs", 111 | "ec2:AttachVpn*", 112 | "ec2:DeleteVpn*", 113 | "ec2:DetachVpn*", 114 | "ec2:DeleteCustomerGateway*", 115 | "ec2:DeleteSnapshot*", 116 | "ec2:DisassociateAddress*", 117 | "ec2:ReleaseAddress*", 118 | "ec2:DeleteInternetGateway", 119 | "ec2:DetachInternetGateway", 120 | "ec2:DeleteNatGateway", 121 | "ec2:DeleteNetworkAcl*", 122 | "ec2:ReplaceNetworkAcl*", 123 | "ec2:PurchaseReservedInstancesOffering", 124 | "ec2:DeleteRoute*", 125 | "ec2:DisableVgw*", 126 | "ec2:DisassociateRoute*", 127 | "ec2:ReplaceRoute*", 128 | "ec2:DeleteSubnet" 129 | ], 130 | "Resource": "*" 131 | }, 132 | { 133 | "Sid": "ProtectCriticalInstances20160801", 134 | "Effect": "Deny", 135 | "Action": [ 136 | "ec2:Stop*", 137 | "ec2:Reboot*", 138 | "ec2:Terminate*", 139 | "ec2:Detach*", 140 | "ec2:*NetworkInterface*", 141 | "ec2:*PrivateIp*" 142 | ], 143 | "Resource": "*", 144 | "Condition": { 145 | "StringEquals": { 146 | "ec2:ResourceTag/Critical":"true" 147 | } 148 | } 149 | }, 150 | { 151 | "Sid": "ProtectBuckets20160801", 152 | "Effect": "Deny", 153 | "Action": [ 154 | "s3:DeleteBucket*" 155 | ], 156 | "Resource": "*", 157 | "Condition": { 158 | "BoolIfExists": { 159 | "aws:MultiFactorAuthPresent": "false" 160 | } 161 | } 162 | } 163 | ] 164 | } 165 | } 166 | }, 167 | 168 | "EnforceMFAPolicy": { 169 | "Type": "AWS::IAM::ManagedPolicy", 170 | "Properties": { 171 | "ManagedPolicyName" : "EnforceMFAforDistructiveAndSensetiveActions", 172 | "Description" : "Block distructive actions without MFA device", 173 | "Path" : "/infosec/policy/", 174 | "PolicyDocument" :{ 175 | "Version": "2012-10-17", 176 | "Statement": [ 177 | { 178 | "Sid": "EnforceMFA4CriticalActions201605", 179 | "Effect": "Deny", 180 | "Action": [ 181 | "ec2:Delete*", 182 | "ec2:Terminate*", 183 | "route53domains:Delete*", 184 | "route53domains:DisableDomainTransferLock", 185 | "route53domains:RetrieveDomainAuthCode", 186 | "route53domains:TransferDomain", 187 | "route53domains:Update*", 188 | "route53:DeleteHostedZone", 189 | "iam:Update*", 190 | "iam:Put*", 191 | "iam:Remove*", 192 | "iam:Detach*", 193 | "iam:Delete*", 194 | "iam:Create*", 195 | "iam:A*" 196 | ], 197 | "Resource": "*", 198 | "Condition": { 199 | "BoolIfExists": { 200 | "aws:MultiFactorAuthPresent": "false" 201 | } 202 | } 203 | } 204 | ] 205 | } 206 | } 207 | }, 208 | 209 | "EnforceAccessFromOfficePolicy": { 210 | "Type": "AWS::IAM::ManagedPolicy", 211 | "Properties": { 212 | "ManagedPolicyName" : "EnforceWorkingFromOffice4SensitiveActions", 213 | "Description" : "Block privilige actions outside of Company's owned subnets", 214 | "Path" : "/infosec/policy/", 215 | "PolicyDocument" :{ 216 | "Version": "2012-10-17", 217 | "Statement": [ 218 | { 219 | "Sid": "EnforceAccessFromOffice4Critical201605", 220 | "Effect": "Deny", 221 | "Action": [ 222 | "ec2:Delete*", 223 | "ec2:Terminate*", 224 | "ec2:Create*", 225 | "ec2:RebootInstances", 226 | "ec2:StopInstances", 227 | "ec2:GetPasswordData", 228 | "ec2:Attach*", 229 | "ec2:Detach*", 230 | "ec2:UnassignPrivateIpAddresses", 231 | "ec2:AuthorizeSecurityGroupEgress", 232 | "ec2:AuthorizeSecurityGroupIngress", 233 | "ec2:Disassociate*", 234 | "route53domains:*", 235 | "route53:*", 236 | "cloudformation:*", 237 | "iam:*" 238 | ], 239 | "Resource": "*", 240 | "Condition": { 241 | "NotIpAddress": { 242 | "aws:SourceIp": "111.222.32.0/20" 243 | } 244 | } 245 | } 246 | ] 247 | } 248 | } 249 | }, 250 | 251 | "InfosecTeamPolicy": { 252 | "Type": "AWS::IAM::ManagedPolicy", 253 | "Properties": { 254 | "ManagedPolicyName" : "InfosecTeamPolicy", 255 | "Description" : "Infosec managed policy that gives Infosec additional permissions compare to default Read-Only access", 256 | "Path" : "/infosec/policy/", 257 | "PolicyDocument" : { 258 | "Version": "2012-10-17", 259 | "Statement": [ 260 | { 261 | "Sid": "AllIamExceptCreateUserAndGroup2016", 262 | "Effect": "Allow", 263 | "Action": [ 264 | "iam:AddRoleToInstanceProfile", 265 | "iam:AddUserToGroup", 266 | "iam:AttachGroupPolicy", 267 | "iam:AttachRolePolicy", 268 | "iam:AttachUserPolicy", 269 | "iam:CreateAccountAlias", 270 | "iam:CreateGroup", 271 | "iam:CreateInstanceProfile", 272 | "iam:CreateLoginProfile", 273 | "iam:CreatePolicy", 274 | "iam:CreatePolicyVersion", 275 | "iam:CreateRole", 276 | "iam:DeleteAccountAlias", 277 | "iam:DeleteAccountPasswordPolicy", 278 | "iam:DeleteGroupPolicy", 279 | "iam:DeleteInstanceProfile", 280 | "iam:DeleteLoginProfile", 281 | "iam:DeletePolicy", 282 | "iam:DeletePolicyVersion", 283 | "iam:DeleteRole", 284 | "iam:DeleteRolePolicy", 285 | "iam:DeleteUserPolicy", 286 | "iam:DetachGroupPolicy", 287 | "iam:DetachRolePolicy", 288 | "iam:DetachUserPolicy", 289 | "iam:GenerateCredentialReport", 290 | "iam:PassRole", 291 | "iam:PutGroupPolicy", 292 | "iam:PutRolePolicy", 293 | "iam:PutUserPolicy", 294 | "iam:RemoveRoleFromInstanceProfile", 295 | "iam:RemoveUserFromGroup", 296 | "iam:SetDefaultPolicyVersion", 297 | "iam:SimulateCustomPolicy", 298 | "iam:SimulatePrincipalPolicy", 299 | "iam:UpdateAccountPasswordPolicy", 300 | "iam:UpdateAssumeRolePolicy", 301 | "iam:UpdateGroup", 302 | "iam:UpdateLoginProfile", 303 | "iam:UpdateServerCertificate", 304 | "iam:UpdateSigningCertificate", 305 | "iam:UpdateUser", 306 | "iam:UploadServerCertificate", 307 | "iam:UploadSigningCertificate" 308 | ], 309 | "Resource": [ 310 | "*" 311 | ] 312 | }, 313 | { 314 | "Sid": "AllKms2016", 315 | "Effect": "Allow", 316 | "Action": [ 317 | "kms:*" 318 | ], 319 | "Resource": [ 320 | "*" 321 | ] 322 | }, 323 | { 324 | "Sid": "AllEc2forInfosecVMs2016", 325 | "Effect": "Allow", 326 | "Action": [ 327 | "ec2:*" 328 | ], 329 | "Condition": { 330 | "StringEquals": { 331 | "ec2:ResourceTag/environment": "infosec" 332 | } 333 | }, 334 | "Resource": [ 335 | "*" 336 | ] 337 | }, 338 | { 339 | "Sid": "CreateInfosecVMs2016", 340 | "Effect": "Allow", 341 | "Action": [ 342 | "ec2:CreateTags", 343 | "ec2:CreateVolume", 344 | "ec2:AllocateAddress", 345 | "ec2:AssociateAddress", 346 | "ec2:RunInstances", 347 | "ec2:StartInstances", 348 | "ec2:AuthorizeSecurityGroupEgress", 349 | "ec2:AuthorizeSecurityGroupIngress", 350 | "ec2:CreateFlowLogs", 351 | "ec2:CreateKeyPair", 352 | "ec2:CreateSecurityGroup", 353 | "ec2:CreateTags", 354 | "ec2:DeleteFlowLogs", 355 | "ec2:DeleteSecurityGroup", 356 | "ec2:ImportKeyPair", 357 | "ec2:RevokeSecurityGroupEgress", 358 | "ec2:RevokeSecurityGroupIngress" 359 | ], 360 | "Resource": [ 361 | "*" 362 | ] 363 | }, 364 | { 365 | "Sid": "AllS3ForInfosecBucket2016", 366 | "Effect": "Allow", 367 | "Action": [ 368 | "s3:*" 369 | ], 370 | "Resource": [ 371 | "arn:aws:s3:::changeme.prod.infosec*" 372 | ] 373 | }, 374 | { 375 | "Sid": "CreateBucket2016", 376 | "Effect": "Allow", 377 | "Action": [ 378 | "s3:CreateBucket", 379 | "s3:PutBucketLogging" 380 | ], 381 | "Resource": [ 382 | "*" 383 | ] 384 | }, 385 | { 386 | "Sid": "AllConfigService2016", 387 | "Effect": "Allow", 388 | "Action": [ 389 | "config:*" 390 | ], 391 | "Resource": [ 392 | "*" 393 | ] 394 | }, 395 | { 396 | "Sid": "AllWafSrvice2016", 397 | "Effect": "Allow", 398 | "Action": [ 399 | "waf:*" 400 | ], 401 | "Resource": [ 402 | "*" 403 | ] 404 | }, 405 | { 406 | "Sid": "AllTrustedAdvisor2016", 407 | "Effect": "Allow", 408 | "Action": [ 409 | "trustedadvisor:*" 410 | ], 411 | "Resource": [ 412 | "*" 413 | ] 414 | }, 415 | { 416 | "Sid": "AllMarketSpace2016", 417 | "Effect": "Allow", 418 | "Action": [ 419 | "aws-marketplace:*" 420 | ], 421 | "Resource": [ 422 | "*" 423 | ] 424 | }, 425 | { 426 | "Sid": "UseAWSlogsButNotDelete2016", 427 | "Effect": "Allow", 428 | "Action": [ 429 | "logs:CreateLogGroup", 430 | "logs:CreateLogStream", 431 | "logs:DeleteMetricFilter", 432 | "logs:DeleteRetentionPolicy", 433 | "logs:DescribeLogGroups", 434 | "logs:DescribeLogStreams", 435 | "logs:DescribeMetricFilters", 436 | "logs:FilterLogEvents", 437 | "logs:GetLogEvents", 438 | "logs:PutLogEvents", 439 | "logs:PutMetricFilter", 440 | "logs:PutRetentionPolicy", 441 | "logs:TestMetricFilter" 442 | ], 443 | "Resource": [ 444 | "*" 445 | ] 446 | }, 447 | { 448 | "Sid": "AllCloudWatch2016", 449 | "Effect": "Allow", 450 | "Action": [ 451 | "cloudwatch:*" 452 | ], 453 | "Resource": [ 454 | "*" 455 | ] 456 | }, 457 | { 458 | "Sid": "AllCodeCommitInfosec2016", 459 | "Effect": "Allow", 460 | "Action": [ 461 | "codecommit:*" 462 | ], 463 | "Resource": { "Fn::Join" : ["", ["arn:aws:codecommit:us-east-1:", {"Ref": "AWS::AccountId"} , ":infosec*" ]]} 464 | }, 465 | { 466 | "Sid": "CodeCommitCreate2016", 467 | "Effect": "Allow", 468 | "Action": [ 469 | "codecommit:CreateRepository" 470 | ], 471 | "Resource": [ 472 | "*" 473 | ] 474 | } 475 | 476 | ] 477 | } 478 | } 479 | }, 480 | 481 | "InfosecGroup": { 482 | "Type": "AWS::IAM::Group", 483 | "DependsOn": ["InfosecTeamPolicy","UserSelfServicePolicy"], 484 | "Properties": { 485 | "GroupName": "Infosec", 486 | "ManagedPolicyArns": [ {"Ref": "InfosecTeamPolicy"}, {"Ref": "UserSelfServicePolicy"}, "arn:aws:iam::aws:policy/ReadOnlyAccess" ], 487 | "Path": "/infosec/groups/" 488 | } 489 | }, 490 | 491 | "DevOpsPolicy": { 492 | "Type": "AWS::IAM::ManagedPolicy", 493 | "Properties": { 494 | "ManagedPolicyName" : "LimidedAccess4DevOps", 495 | "Description" : "DevOps managed policy that gives additional permissions compare to default Power user access", 496 | "Path" : "/infosec/policy/", 497 | "PolicyDocument" :{ 498 | "Version": "2012-10-17", 499 | "Statement": [ 500 | { 501 | "Sid": "CreateDeleteUserandGroup2016", 502 | "Effect": "Allow", 503 | "Action": [ 504 | "iam:CreateGroup", 505 | "iam:CreateUser", 506 | "iam:DeleteUser", 507 | "iam:DeleteGroup", 508 | "iam:PassRole", 509 | "iam:List*", 510 | "iam:Get*" 511 | ], 512 | "Resource": [ 513 | "*" 514 | ] 515 | } 516 | ] 517 | } 518 | } 519 | }, 520 | "DevOpsGroup": { 521 | "Type": "AWS::IAM::Group", 522 | "DependsOn": ["DevOpsPolicy","UserSelfServicePolicy", "ProtectProdEnviroment"], 523 | "Properties": { 524 | "GroupName": "DevOps", 525 | "ManagedPolicyArns": [ {"Ref": "DevOpsPolicy"}, {"Ref": "UserSelfServicePolicy"}, {"Ref": "ProtectProdEnviroment"}, "arn:aws:iam::aws:policy/PowerUserAccess" ], 526 | "Path": "/infosec/groups/" 527 | } 528 | }, 529 | 530 | 531 | "DomainJoin": { 532 | "Type": "AWS::IAM::Role", 533 | "Properties": { 534 | "RoleName": "DomainJoinRole", 535 | "AssumeRolePolicyDocument": { 536 | "Version" : "2012-10-17", 537 | "Statement": [ { 538 | "Effect": "Allow", 539 | "Principal": { 540 | "Service": [ "ec2.amazonaws.com" ] 541 | }, 542 | "Action": [ "sts:AssumeRole" ] 543 | } ] 544 | }, 545 | "Path": "/infosec/services/", 546 | "ManagedPolicyArns" : ["arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"] 547 | } 548 | }, 549 | 550 | "DomainJoinInstanceProfile" : { 551 | "Type" : "AWS::IAM::InstanceProfile", 552 | "Properties" : { 553 | "InstanceProfileName": "DomainJoinInstanceProfile", 554 | "Path" : "/infosec/services/", 555 | "Roles" : [{ "Ref": "DomainJoin" }] 556 | } 557 | }, 558 | 559 | "ChefServerPolicy": { 560 | "DependsOn": ["DomainJoin"], 561 | "Type": "AWS::IAM::ManagedPolicy", 562 | "Properties": { 563 | "ManagedPolicyName" : "ChefServerPolicy", 564 | "Description" : "Automation MasterServer Policy", 565 | "Path" : "/infosec/policy/", 566 | "PolicyDocument" :{ 567 | "Version": "2012-10-17", 568 | "Statement": [ 569 | { 570 | "Sid": "IamMinimumAccess2016", 571 | "Effect": "Allow", 572 | "Action": [ 573 | "iam:PassRole", 574 | "iam:ListInstanceProfiles" 575 | ], 576 | "Resource": [{"Fn::GetAtt" : ["DomainJoin", "Arn"]}] 577 | }, 578 | { 579 | "Sid": "AllForSSM2016", 580 | "Effect": "Allow", 581 | "Action": [ 582 | "ssm:*" 583 | ], 584 | "Resource": [ 585 | "*" 586 | ] 587 | }, 588 | { 589 | "Sid": "CodeCommitLimited2016", 590 | "Effect": "Allow", 591 | "Action": [ 592 | "codecommit:BatchGetRepositories", 593 | "codecommit:CreateBranch", 594 | "codecommit:Get*", 595 | "codecommit:GitPull", 596 | "codecommit:GitPush", 597 | "codecommit:List*", 598 | "codecommit:Update*" 599 | ], 600 | "Resource": { "Fn::Join" : ["", ["arn:aws:codecommit:us-east-1:", {"Ref": "AWS::AccountId"} , ":chef*" ]]} 601 | } 602 | 603 | ] 604 | } 605 | } 606 | }, 607 | 608 | "ChefServer": { 609 | "Type": "AWS::IAM::Role", 610 | "DependsOn": [ "ChefServerPolicy"], 611 | "Properties": { 612 | "RoleName": "ChefServerRole", 613 | "AssumeRolePolicyDocument": { 614 | "Version" : "2012-10-17", 615 | "Statement": [ { 616 | "Effect": "Allow", 617 | "Principal": { 618 | "Service": [ "ec2.amazonaws.com" ] 619 | }, 620 | "Action": [ "sts:AssumeRole" ] 621 | } ] 622 | }, 623 | "Path": "/infosec/services/", 624 | "ManagedPolicyArns" : [{"Ref": "ChefServerPolicy"}, "arn:aws:iam::aws:policy/AmazonEC2FullAccess"] 625 | } 626 | }, 627 | 628 | "ChefServerInstanceProfile" : { 629 | "Type" : "AWS::IAM::InstanceProfile", 630 | "Properties" : { 631 | "InstanceProfileName": "ChefServerInstanceProfile", 632 | "Path" : "/infosec/services/", 633 | "Roles" : [{ "Ref": "ChefServer" }] 634 | } 635 | }, 636 | 637 | 638 | "SplunkAccessPolicy": { 639 | "Type": "AWS::IAM::ManagedPolicy", 640 | "Properties": { 641 | "ManagedPolicyName" : "SplunkAccessPolicy", 642 | "Description" : "Splunk systems access policy", 643 | "Path" : "/infosec/policy/", 644 | "PolicyDocument" :{ 645 | "Version": "2012-10-17", 646 | "Statement": [ 647 | { "Sid": "SplunktoSQS", 648 | "Effect": "Allow", 649 | "Action": [ "sqs:ListQueues", "sqs:ReceiveMessage", "sqs:GetQueueAttributes", "sqs:SendMessage", "sqs:GetQueueUrl", "sqs:DeleteMessage" ], 650 | "Resource": [{"Ref": "SQSCloudTrailArn" } , {"Ref": "SQSAWSConfigArn" }] 651 | }, 652 | { "Sid": "SplunktoAWSConfig", 653 | "Effect": "Allow", 654 | "Action": [ "config:DeliverConfigSnapshot" ], 655 | "Resource": "*" 656 | } 657 | ] 658 | } 659 | } 660 | }, 661 | 662 | 663 | "ITOrganizationAccountAccessRole": { 664 | "Type": "AWS::IAM::Role", 665 | "Properties": { 666 | "RoleName": "ITOrganizationAccountAccessRole", 667 | "AssumeRolePolicyDocument": { 668 | "Version": "2012-10-17", 669 | "Statement": [ 670 | { 671 | "Effect": "Allow", 672 | "Principal": { 673 | "AWS": { "Fn::Join" : ["", ["arn:aws:iam::", {"Ref": "MasterAccount"} , ":root" ]]} 674 | }, 675 | "Action": "sts:AssumeRole", 676 | "Condition": { 677 | "Bool": { 678 | "aws:MultiFactorAuthPresent": "true" 679 | } 680 | } 681 | } 682 | ] 683 | }, 684 | "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/AdministratorAccess" ] 685 | } 686 | }, 687 | 688 | "VPCFlowLogRole": { 689 | "Type": "AWS::IAM::Role", 690 | "Properties": { 691 | "RoleName": "VPCFlowLogRole", 692 | "AssumeRolePolicyDocument": { 693 | "Version" : "2012-10-17", 694 | "Statement": [ { 695 | "Effect": "Allow", 696 | "Principal": { 697 | "Service": [ "vpc-flow-logs.amazonaws.com" ] 698 | }, 699 | "Action": [ "sts:AssumeRole" ] 700 | } ] 701 | }, 702 | "Path": "/infosec/services/", 703 | "Policies" : [{ "PolicyName": "VPCFlowLog", "PolicyDocument": { 704 | "Version": "2012-10-17", 705 | "Statement": [ 706 | { 707 | "Sid": "VOCFlowLog2014110", 708 | "Effect": "Allow", 709 | "Action": [ 710 | "logs:CreateLogGroup", 711 | "logs:CreateLogStream", 712 | "logs:PutLogEvents", 713 | "logs:DescribeLogGroups", 714 | "logs:DescribeLogStreams" 715 | ], 716 | "Resource": "*" 717 | 718 | } ] 719 | }}] 720 | } 721 | }, 722 | 723 | "SuperAdminGroup": { 724 | "Type": "AWS::IAM::Group", 725 | "Properties": { 726 | "GroupName": "SuperAdminGroup", 727 | "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/AdministratorAccess" ], 728 | "Path": "/infosec/groups/" 729 | } 730 | }, 731 | 732 | "ServiceAccountsGroup": { 733 | "Type": "AWS::IAM::Group", 734 | "Properties": { 735 | "GroupName": "ServiceAccountsGroup", 736 | "Path": "/infosec/groups/" 737 | } 738 | }, 739 | 740 | 741 | "SplunkUser": { 742 | "Type": "AWS::IAM::User", 743 | "DependsOn": ["ServiceAccountsGroup", "SplunkAccessPolicy"], 744 | "Properties": { 745 | "UserName": "SplunkUser", 746 | "Groups": [ { "Ref": "ServiceAccountsGroup" }], 747 | "ManagedPolicyArns": [ {"Ref": "SplunkAccessPolicy"}, "arn:aws:iam::aws:policy/ReadOnlyAccess" ], 748 | "Path": "/infosec/serviceaccounts/" 749 | } 750 | }, 751 | "SplunkUserKey": { 752 | "Type": "AWS::IAM::AccessKey", 753 | "DependsOn": ["SplunkUser"], 754 | "Properties": { 755 | "Serial": 1, 756 | "Status": "Active", 757 | "UserName": { "Ref": "SplunkUser" } 758 | } 759 | }, 760 | 761 | "SMTPUser": { 762 | "Type": "AWS::IAM::User", 763 | "DependsOn": ["ServiceAccountsGroup"], 764 | "Properties": { 765 | "UserName": "SMTPUser", 766 | "Groups": [ { "Ref": "ServiceAccountsGroup" }], 767 | "Path": "/infosec/serviceaccounts/", 768 | "Policies" : [{ "PolicyName": "SMTPUser", "PolicyDocument": { 769 | "Version": "2012-10-17", 770 | "Statement": [ 771 | { 772 | "Sid": "SMTPSES2014110", 773 | "Effect": "Allow", 774 | "Action": [ 775 | "ses:SendRawEmail" 776 | ], 777 | "Resource": "*" 778 | } ] 779 | }}] 780 | 781 | } 782 | }, 783 | 784 | "SMTPUserKey": { 785 | "Type": "AWS::IAM::AccessKey", 786 | "DependsOn": ["SMTPUser"], 787 | "Properties": { 788 | "Serial": 1, 789 | "Status": "Active", 790 | "UserName": { "Ref": "SMTPUser" } 791 | } 792 | } 793 | 794 | }, 795 | "Outputs" : { 796 | 797 | "SplunkUserAccessKeyId" : { 798 | "Description": "SplunkUser AccessKeyId", 799 | "Value" : {"Ref": "SplunkUserKey"} 800 | }, 801 | "SplunkUserSecretKey" : { 802 | "Description": "SplunkMUser Secret Key", 803 | "Value" : {"Fn::GetAtt":["SplunkUserKey","SecretAccessKey"]} 804 | }, 805 | 806 | "SMTPUserAccessKeyId" : { 807 | "Description": "SMTPUser AccessKeyId", 808 | "Value" : {"Ref": "SMTPUserKey"} 809 | }, 810 | "SMTPUserSecretKey" : { 811 | "Description": "SMTPMUser Secret Key", 812 | "Value" : {"Fn::GetAtt":["SMTPUserKey","SecretAccessKey"]} 813 | } 814 | 815 | } 816 | } 817 | -------------------------------------------------------------------------------- /cf/security.global.cf/security.global.yaml: -------------------------------------------------------------------------------- 1 | # InfosecStack Version 1.28 2 | # Desc: Done CIS benchmark monitoring options 3 | 4 | AWSTemplateFormatVersion: '2010-09-09' 5 | Description: Enabling and configuring Global AWS Secutiy settings 6 | Parameters: 7 | AccountNickname: 8 | Description: 'AWS Account nickname(purpose) to deploy. ' 9 | Type: String 10 | CompanyName: 11 | Description: CompanyName. 12 | Type: String 13 | MasterAccount: 14 | Description: AWS Organization Master Account. 15 | Type: String 16 | Resources: 17 | # Enabling and configuring Global CloudTrail 18 | CloudTrailGlobal: 19 | Type: AWS::CloudFormation::Stack 20 | Properties: 21 | TemplateURL: !Join ['', ['https://s3.amazonaws.com/com.', !Ref 'CompanyName', 22 | ., !Ref 'AccountNickname', .cloudform/cloudtrail.global.json]] 23 | Parameters: 24 | cloudtrailBucketName: !Join ['', [com., !Ref 'CompanyName', ., !Ref 'AccountNickname', 25 | .infosec.cloudtrail]] 26 | TimeoutInMinutes: '60' 27 | 28 | # Enabling AWS config service TBD: More configuration rules to add 29 | AWSConfigGlobal: 30 | Type: AWS::CloudFormation::Stack 31 | Properties: 32 | TemplateURL: !Join ['', ['https://s3.amazonaws.com/com.', !Ref 'CompanyName', 33 | ., !Ref 'AccountNickname', .cloudform/awsconfig.global.json]] 34 | Parameters: 35 | AWSConfigBucketName: !Join ['', [com., !Ref 'CompanyName', ., !Ref 'AccountNickname', 36 | .infosec.awsconfig]] 37 | TimeoutInMinutes: '60' 38 | 39 | # Creating Filter Patterns for CloudWatchLogs, CloudWatch metrics , correspoing alalrms and sns notifications 40 | CloudWarchAlarms: 41 | Type: AWS::CloudFormation::Stack 42 | Properties: 43 | TemplateURL: !Join ['', ['https://s3.amazonaws.com/com.', !Ref 'CompanyName', 44 | ., !Ref 'AccountNickname', .cloudform/cloudtrailalarms.global.json]] 45 | Parameters: 46 | LogGroupName: !GetAtt 'CloudTrailGlobal.Outputs.CloudWatchLogsLogGroup' 47 | CompanyName: !Ref 'CompanyName' 48 | AccountNickname: !Ref 'AccountNickname' 49 | TimeoutInMinutes: '60' 50 | 51 | # Creating BestPractice Security Policies, Groups and Security-specific users 52 | IAM: 53 | Type: AWS::CloudFormation::Stack 54 | DependsOn: 55 | - AWSConfigGlobal 56 | - CloudTrailGlobal 57 | Properties: 58 | TemplateURL: !Join ['', ['https://s3.amazonaws.com/com.', !Ref 'CompanyName', 59 | ., !Ref 'AccountNickname', .cloudform/iam.global.json]] 60 | Parameters: 61 | AccountNickname: !Ref 'AccountNickname' 62 | MasterAccount: !Ref 'MasterAccount' 63 | SQSCloudTrailArn: !GetAtt 'CloudTrailGlobal.Outputs.SQSCloudTrailArn' 64 | SQSAWSConfigArn: !GetAtt 'AWSConfigGlobal.Outputs.SQSAWSConfigArn' 65 | TimeoutInMinutes: '60' 66 | 67 | 68 | Outputs: 69 | CloudTrailGlobalRef: 70 | Value: !Ref 'CloudTrailGlobal' 71 | CloudWatchLogsLogGroupARN: 72 | Value: !GetAtt 'CloudTrailGlobal.Outputs.CloudWatchLogsLogGroup' 73 | SQSAWSConfigName: 74 | Value: !GetAtt 'AWSConfigGlobal.Outputs.SQSAWSConfigName' 75 | -------------------------------------------------------------------------------- /scripts.aws/aws_enforce_password_policy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import boto3 4 | import pprint 5 | import json 6 | import sys 7 | from optparse import OptionParser 8 | 9 | __author__ = "Ihor Kravchuk" 10 | __license__ = "GPL" 11 | __version__ = "0.1.0" 12 | __maintainer__ = "Ihor Kravchuk" 13 | __email__ = "igor@it-security.ca" 14 | __status__ = "Development" 15 | 16 | class bcolors: 17 | HEADER = '\033[95m' 18 | OKBLUE = '\033[94m' 19 | OKGREEN = '\033[92m' 20 | WARNING = '\033[93m' 21 | FAIL = '\033[91m' 22 | ENDC = '\033[0m' 23 | BOLD = '\033[1m' 24 | UNDERLINE = '\033[4m' 25 | 26 | parser = OptionParser() 27 | parser.add_option('-P', '--profile', type='string', dest='aws_profile', help='Please specify AWS CLI profile') 28 | parser.add_option('-A', '--action', type='string', dest='action', default="get", help='Please specify action: set or get') 29 | 30 | (options, args) = parser.parse_args() 31 | 32 | # Parsing comman line input or asking user for missing information 33 | # Checking AWS credentials in profile 34 | 35 | if options.aws_profile is None : 36 | parser.print_help() 37 | sys.exit(-1) 38 | else: 39 | try: 40 | boto3.setup_default_session(profile_name=options.aws_profile) 41 | except: 42 | print bcolors.FAIL+ "Your credentials in the profile (", options.aws_profile, " ) do not allow AWS access"+bcolors.ENDC 43 | parser.print_help() 44 | sys.exit(-1) 45 | 46 | iam=boto3.client('iam') 47 | 48 | if options.action == "set" : 49 | response = iam.update_account_password_policy( 50 | MinimumPasswordLength=15, 51 | RequireSymbols=True, 52 | RequireNumbers=True, 53 | RequireUppercaseCharacters=True, 54 | AllowUsersToChangePassword=True, 55 | RequireLowercaseCharacters=True, 56 | MaxPasswordAge=90, 57 | PasswordReusePrevention=24, 58 | HardExpiry=False 59 | ) 60 | if response["ResponseMetadata"]["HTTPStatusCode"] == 200 : 61 | print bcolors.OKGREEN+ "Password policy successfully enforced on the AWS account: "+ str(options.aws_profile) +bcolors.ENDC 62 | 63 | 64 | print bcolors.OKGREEN+ "Current password policy is: " +bcolors.ENDC 65 | pprint.pprint(iam.get_account_password_policy()["PasswordPolicy"]) 66 | -------------------------------------------------------------------------------- /scripts.aws/aws_secgroup_viewer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import json 4 | import re 5 | import sys 6 | import os 7 | import pprint 8 | from operator import itemgetter 9 | 10 | __author__ = "Ihor Kravchuk" 11 | __license__ = "GPL" 12 | __version__ = "0.9.0" 13 | __maintainer__ = "Ihor Kravchuk" 14 | __email__ = "igor@it-security.ca" 15 | __status__ = "Development" 16 | 17 | # command line highlite helper class 18 | class bcolors: 19 | HEADER = '\033[95m' 20 | OKBLUE = '\033[94m' 21 | OKGREEN = '\033[92m' 22 | WARNING = '\033[93m' 23 | FAIL = '\033[91m' 24 | ENDC = '\033[0m' 25 | BOLD = '\033[1m' 26 | UNDERLINE = '\033[4m' 27 | 28 | # menu function - creates simple numbered menu and returns user's choice 29 | def menu(items, message): 30 | print "\n" 31 | for index, item in enumerate(items): 32 | print index,") ", item 33 | return int(raw_input(message)) 34 | 35 | def aws_etxract_rules(resource, direction, rule): 36 | firewall_rule = dict() 37 | firewall_rule["Direction"] = direction 38 | try: 39 | firewall_rule["IpProtocol"] = rule["IpProtocol"] 40 | if direction == "ingress": 41 | if rule.get("CidrIp") != None: firewall_rule["Source"] = rule["CidrIp"] 42 | else: firewall_rule["Source"] = rule["SourceSecurityGroupId"] 43 | elif direction == "egress": 44 | if rule.get("CidrIp") != None: firewall_rule["Destination"] = rule["CidrIp"] 45 | else: firewall_rule["Destination"] = rule["DestinationSecurityGroupId"] 46 | if firewall_rule["IpProtocol"] == "-1": 47 | firewall_rule["DestinFromPort"] = "ANY" 48 | firewall_rule["DestinToPort"] = "ANY" 49 | else: 50 | firewall_rule["DestinFromPort"] = rule["FromPort"] 51 | firewall_rule["DestinToPort"] = rule["ToPort"] 52 | except: 53 | print bcolors.WARNING + "---Warning. Not correct resource description. Missing parameter", resource, " rule: ", str(rule) + bcolors.ENDC 54 | 55 | return firewall_rule 56 | 57 | 58 | # Loading data form file 59 | # loading JSON 60 | if len(sys.argv)<2 or not os.path.isfile(sys.argv[1]): 61 | print "Please specify existing CloudFormation template file name as argument" 62 | sys.exit(1) 63 | content = open(sys.argv[1], "r") 64 | data = content.read() 65 | content.close() 66 | try: 67 | js = json.loads(data) 68 | except: 69 | print "Not a valid JSON file" 70 | sys.exit(1) 71 | 72 | 73 | # extracting security groups and firewall rules from JSON and convering it to the IhorFirewallRuleFormat: 74 | # firewall_rule = {"Direction": "ingress/egress", "IpProtocol": "protocol_name", "Source":"CidrIp/sec_group/url", "SourceFromPort":"port", "SourceToPort":"port", "DestinFromPort":"port", "DestinToPort":"port", "Destination":"CidrIp/sec_group/url"} 75 | # security_group = [firewall_rule1, firewall_rule2, firewall_rule3] 76 | # all security groups = { "sec_group_linux": "security_group", "sec_group_windows": "security_group", "sec_group_oracle": "security_group"} 77 | security_groups = dict() 78 | security_group = list() 79 | 80 | # AMAZON CLOUDFORMATION TYPE1 81 | # extracting all firewall rules that defined as a Resources in the template file 82 | for resource in js["Resources"]: 83 | # Extracting egress and ingress rules 84 | if js["Resources"][resource]["Type"] == "AWS::EC2::SecurityGroupIngress": 85 | rule_direction = "ingress" 86 | elif js["Resources"][resource]["Type"] == "AWS::EC2::SecurityGroupEgress": 87 | rule_direction = "egress" 88 | else: 89 | continue 90 | try: 91 | sec_group_name = js["Resources"][resource]["Properties"]["GroupId"]["Ref"] 92 | except: 93 | print bcolors.WARNING + "---Warning. Firewall rule has not security group referenece", resource + bcolors.ENDC 94 | continue 95 | security_groups[sec_group_name] = security_groups.get(sec_group_name, []) 96 | security_groups[sec_group_name].append(aws_etxract_rules(resource, rule_direction, js["Resources"][resource]["Properties"])) 97 | #print json.dumps(security_groups, indent=4) 98 | 99 | 100 | # AMAZON CLOUDFORMATION TYPE2 101 | # extracting all firewall rules that defined as aprt of Security resource in the template file 102 | for resource in js["Resources"]: 103 | # Extracting Ingress rules inside securuity group 104 | if js["Resources"][resource]["Type"] == "AWS::EC2::SecurityGroup" and js["Resources"][resource]["Properties"].get("SecurityGroupIngress") != None: 105 | sec_group_name = resource 106 | for rule in js["Resources"][resource]["Properties"]["SecurityGroupIngress"]: 107 | security_groups[sec_group_name] = security_groups.get(sec_group_name, []) 108 | security_groups[sec_group_name].append(aws_etxract_rules(resource, "ingress", rule)) 109 | if js["Resources"][resource]["Type"] == "AWS::EC2::SecurityGroup" and js["Resources"][resource]["Properties"].get("SecurityGroupEgress") != None: 110 | sec_group_name = resource 111 | for rule in js["Resources"][resource]["Properties"]["SecurityGroupEgress"]: 112 | security_groups[sec_group_name] = security_groups.get(sec_group_name, []) 113 | security_groups[sec_group_name].append(aws_etxract_rules(resource, "egress", rule)) 114 | 115 | 116 | # User interface 117 | status = 1 118 | while status != 0: 119 | list_of_groups =security_groups.keys() 120 | sec_num = menu(list_of_groups, "Enter the number of the security group >> ") 121 | print "\n Security Group", list_of_groups[sec_num], "\n" 122 | print bcolors.OKBLUE + "{:<15} {:<15} {:<15} {:<15} {:<15} {:<15} {:<15} {:<15}".format("Direction", "IpProtocol", "Source", "SourceFromPort", "SourceToPort", "DestinToPort", "DestinFromPort", "Destination" ) + bcolors.ENDC 123 | sorted_rules = sorted(security_groups[list_of_groups[sec_num]], key=itemgetter('Direction'), reverse=True) 124 | for rule in sorted_rules: 125 | print "{:<15} {:<15} {:<15} {:<15} {:<15} {:<15} {:<15} {:<15}".format(rule.get("Direction"), rule.get("IpProtocol"), rule.get("Source"), rule.get("SourceFromPort"), rule.get("SourceToPort"), rule.get("DestinToPort"), rule.get("DestinFromPort"), rule.get("Destination") ) 126 | status = menu(["Exit", "Check another security group"], " >> ") 127 | -------------------------------------------------------------------------------- /scripts.aws/aws_test_bucket.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import boto3 4 | from botocore.exceptions import ClientError 5 | import pprint 6 | import json 7 | import sys 8 | from optparse import OptionParser 9 | from termcolor import colored, cprint 10 | 11 | __author__ = "Ihor Kravchuk" 12 | __license__ = "GPL" 13 | __version__ = "0.3.0" 14 | __maintainer__ = "Ihor Kravchuk" 15 | __email__ = "igor@it-security.ca" 16 | __status__ = "Development" 17 | 18 | # Cheking black box bucket security status 19 | def check_bucket(bucket_name): 20 | # Trying to retrive index.html from the bucket 21 | try: 22 | response = s3.get_object(Bucket=bucket_name, Key='index.html') 23 | bucket_status_code = "S3WebSite" 24 | except ClientError as ex: 25 | # print ex.response 26 | bucket_status_code= ex.response['Error']['Code'] 27 | return bucket_status_code 28 | 29 | # Cheking Authenticated bucket security status 30 | def check_auth_bucket(bucket_name): 31 | # Let's check S3 static web site hosting status 32 | try: 33 | website_status =s3.get_bucket_website(Bucket=bucket_name) 34 | bucket_status_code = "S3WebSite!" 35 | return bucket_status_code 36 | except ClientError as ex: 37 | bucket_status_code = ex.response['Error']['Code'] 38 | # Let's try to get bucket ACL and Policy 39 | # ACL 40 | try: 41 | bucket_acl = s3.get_bucket_acl(Bucket=bucket_name) 42 | for grant in bucket_acl["Grants"]: 43 | if grant["Grantee"]["Type"] == "Group" and "AllUsers" in grant["Grantee"].get("URI"): 44 | bucket_status_code = "AllUsersAccess" 45 | return bucket_status_code 46 | elif grant["Grantee"]["Type"] == "Group" and "AuthenticatedUsers" in grant["Grantee"].get("URI"): 47 | bucket_status_code = "AllAuthUsersAccess" 48 | return bucket_status_code 49 | 50 | except ClientError as ex: 51 | if ex.response['Error']['Code'] == "AccessDenied": 52 | bucket_status_code = "AccessDenied2ACL" 53 | else: 54 | bucket_status_code ="Can'tVerify" 55 | # cprint ("Weird"+ str(ex.response['Error']), "red") 56 | #Policy 57 | try: 58 | bucket_policy = s3.get_bucket_policy(Bucket=bucket_name) 59 | bucket_policy_j = json.loads(bucket_policy["Policy"]) 60 | for statement in bucket_policy_j["Statement"]: 61 | if (statement.get("Condition") is None and 62 | statement["Effect"] == "Allow" and 63 | ("'*'" in str(statement["Principal"]) or statement["Principal"] == "*")): 64 | bucket_status_code = str(statement["Action"]) 65 | return bucket_status_code 66 | # Policy exists but not allow public access 67 | bucket_status_code = "NoPublicAccess" 68 | except ClientError as ex: 69 | if ex.response['Error']['Code'] == "NoSuchBucketPolicy": 70 | bucket_status_code = "NoSuchBucketPolicy" 71 | elif ex.response['Error']['Code'] == "AccessDenied": 72 | bucket_status_code = "AccessDenied2Policy" 73 | 74 | else: 75 | bucket_status_code ="Can'tVerify" 76 | # cprint("Weird"+ str(ex.response['Error']), "red") 77 | # return status code 78 | return bucket_status_code 79 | 80 | 81 | def print_bucket_status(bucket, status): 82 | # Public access exists 83 | if status == "S3WebSite": 84 | print colored("Bucket: "+ bucket, color="yellow"), " - Found index.html, most probably S3 static web hosting is enabled" 85 | elif status == "S3WebSite!": 86 | print colored("Bucket: "+ bucket, color="yellow"), " - S3 static web hosting is enabled on the bucket" 87 | elif status == "NoSuchKey": 88 | print colored("Bucket: "+ bucket, color="red"), " - Bucket exists, publicly available and no S3 static web hosting, most probably misconfigured! " 89 | elif status == "AllUsersAccess" or status == "AllAuthUsersAccess": 90 | print colored("Bucket: "+ bucket, color="red"), " - Bucket exists and publicly ( "+ status+ " ) "+ " available. Reason: Bucket ACL ! " 91 | elif "s3:" in status: 92 | print colored("Bucket: "+ bucket, color="red"), " - Bucket exists and publicly ( "+ status+ " ) "+ " available. Reason: Bucket Policy ! " 93 | # Can't check due to the access restrictions: 94 | elif status == "AccessDenied2ACL" or status == "AccessDenied2Policy": 95 | print colored("Bucket: "+ bucket, color="yellow"), " - Bucket exists, but can't verify policy or ACL due to the: "+ status 96 | # No public acess 97 | elif status == "AccessDenied" or status == "NoSuchBucketPolicy" or status == "NoPublicAccess": 98 | print colored("Bucket: "+ bucket, color="green"), " - No public access detected" 99 | # No bucket - no problem 100 | elif status == "NoSuchBucket": 101 | print colored("Bucket: "+ bucket, color="green"), " - The specified bucket does not exist" 102 | # Can't really verify due to the API Error 103 | elif status == "Can'tVerify" or status == "InvalidRequest": 104 | print colored("Bucket: "+ bucket, color="yellow"), "- Can't really verify due to the API Error" 105 | else: 106 | cprint("Bucket: "+ bucket+ "----"+ status, "yellow") 107 | return 108 | 109 | parser = OptionParser() 110 | parser.add_option('-P', '--profile', type='string', dest='aws_profile', help='Please specify AWS CLI profile') 111 | parser.add_option('-B', '--bucket', type='string', dest='bucket', help='Please provide bucket name') 112 | parser.add_option('-F', '--file', type='string', dest='file', help='Optional: file with bucket list to check') 113 | 114 | (options, args) = parser.parse_args() 115 | 116 | #seting some variables 117 | buckets = set() 118 | buckets_statuses = {} 119 | 120 | #Parsing comman line input or asking user for missing information 121 | if options.aws_profile is None : 122 | parser.print_help() 123 | sys.exit(-1) 124 | elif options.bucket is None and options.file is None: 125 | parser.print_help() 126 | sys.exit(-1) 127 | else: 128 | try: 129 | #Checking AWS credentials in profile 130 | boto3.setup_default_session(profile_name=options.aws_profile) 131 | except: 132 | cprint("Your credentials in the profile ("+ options.aws_profile+ " ) do not allow AWS access", color="red") 133 | parser.print_help() 134 | sys.exit(-1) 135 | 136 | # Batch mode: working with files 137 | 138 | if options.file is not None and options.file != "aws": 139 | content = open(options.file, "r") 140 | for s3bucket in content: 141 | buckets.add(s3bucket.strip()) 142 | content.close() 143 | else: buckets.add(options.bucket) 144 | 145 | # Getting list of the buckets in the account provided to choose scan mode: 146 | try: 147 | s3res = boto3.resource('s3') 148 | bucket_iterator = s3res.buckets.all() 149 | buckets_in_account = set([bucket.name for bucket in bucket_iterator]) 150 | except: 151 | cprint("Your credentials in the profile ("+ options.aws_profile+ " ) do not allow access to enumerate buckets", color="red") 152 | parser.print_help() 153 | sys.exit(-1) 154 | 155 | # Own account mode, getting list of buckets in the account 156 | 157 | if options.bucket is None and options.file == "aws": 158 | buckets = buckets_in_account 159 | 160 | s3=boto3.client('s3') 161 | 162 | # Cheking bucket statuses 163 | for bucket in buckets: 164 | print "Checking bucket: "+bucket 165 | #Choosing what scan to perform 166 | if bucket in buckets_in_account: 167 | buckets_statuses[bucket] = check_auth_bucket(bucket) 168 | else: 169 | buckets_statuses[bucket] = check_bucket(bucket) 170 | # print_bucket_status(bucket, buckets_statuses[bucket]) 171 | 172 | # Printing sorted results 173 | for bucket, buckets_status in sorted(buckets_statuses.iteritems(), key=lambda (k,v): (v,k)): 174 | print_bucket_status(bucket, buckets_status) 175 | -------------------------------------------------------------------------------- /scripts.aws/s3_enc_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import boto3 4 | import pprint 5 | import sys 6 | import json 7 | from optparse import OptionParser 8 | import progressbar as pb 9 | 10 | 11 | __author__ = "Ihor Kravchuk" 12 | __license__ = "GPL" 13 | __version__ = "2.0.0" 14 | __maintainer__ = "Ihor Kravchuk" 15 | __email__ = "igor@it-security.ca" 16 | __status__ = "Development" 17 | 18 | # menu function - creates simple numbered menu and returns user's choice 19 | def menu(items, message): 20 | print "\n" 21 | for index, item in enumerate(items): 22 | if encryption_enforced(item): 23 | print index,") ", item, bcolors.OKGREEN + "Encryption enforced: Yes" + bcolors.ENDC 24 | else: 25 | print index,") ", item, bcolors.WARNING + "Encryption enforced: NO" + bcolors.ENDC 26 | return int(raw_input(message)) 27 | 28 | # Checking if encryption is enforced on the bucket level 29 | def encryption_enforced(bucket_name): 30 | client = boto3.client('s3') 31 | try: 32 | bucket_policy = client.get_bucket_policy(Bucket=bucket_name) 33 | bucket_policy_j = json.loads(bucket_policy["Policy"]) 34 | # print json.dumps(bucket_policy_j, indent=4, sort_keys=True) 35 | enc_enforced = False 36 | for statement in bucket_policy_j["Statement"]: 37 | if (statement["Action"] == "s3:PutObject" and 38 | statement.get("Condition") != None and 39 | statement["Effect"] == "Deny"): 40 | if "s3:x-amz-server-side-encryption" in statement["Condition"].get("StringNotEquals"): 41 | enc_enforced = True 42 | break 43 | except: 44 | enc_enforced = False 45 | return enc_enforced 46 | 47 | class bcolors: 48 | HEADER = '\033[95m' 49 | OKBLUE = '\033[94m' 50 | OKGREEN = '\033[92m' 51 | WARNING = '\033[93m' 52 | FAIL = '\033[91m' 53 | ENDC = '\033[0m' 54 | BOLD = '\033[1m' 55 | UNDERLINE = '\033[4m' 56 | 57 | 58 | parser = OptionParser() 59 | parser.add_option('-B', '--bucket', type='string', dest='bucket_name', help='Please specify bucket name') 60 | parser.add_option('-P', '--profile', type='string', dest='aws_profile', default="default", help='Please specify AWS CLI profile') 61 | parser.add_option('-F', '--file', type='string', dest='results_file', help='Optional: Specify file to save results') 62 | 63 | (options, args) = parser.parse_args() 64 | 65 | # Parsing comman line input or asking user for missing information 66 | # Checking AWS credentials in profile 67 | if options.aws_profile is None : 68 | parser.print_help() 69 | sys.exit(-1) 70 | else: 71 | try: 72 | boto3.setup_default_session(profile_name=options.aws_profile) 73 | except: 74 | print bcolors.FAIL+ "Your credentials in the profile (", options.aws_profile, " ) do not allow AWS access"+bcolors.ENDC 75 | parser.print_help() 76 | sys.exit(-1) 77 | # Getting bucket name from user or from cli options 78 | if options.bucket_name is None : 79 | try: 80 | s3 = boto3.resource('s3') 81 | bucket_iterator = s3.buckets.all() 82 | buckets_list = [bucket.name for bucket in bucket_iterator] 83 | bucket_name = buckets_list[menu(buckets_list, "Please choose a bucket to scan : ")] 84 | print "Bucket to be scanned: "+ bcolors.WARNING + bucket_name +bcolors.ENDC 85 | except: 86 | parser.print_help() 87 | print bcolors.FAIL+ "Your credentials in profile (", options.aws_profile, ") do not grant access to the buckets"+bcolors.ENDC 88 | sys.exit(-1) 89 | else: 90 | bucket_name = options.bucket_name 91 | # Print Encryption policy status for the bucket 92 | if encryption_enforced(bucket_name): 93 | print bcolors.OKGREEN + "This bucket has encryption enforced for the objects upload " +bcolors.ENDC 94 | else: 95 | print bcolors.FAIL + "This bucket has NO encryption enforced" +bcolors.ENDC 96 | 97 | 98 | # Analising bucket content 99 | 100 | #initialize widgets 101 | widgets = ['Scanning S3 bucket ', pb.Percentage(), ' ', 102 | pb.Bar(marker=pb.RotatingMarker()), ' ', pb.ETA()] 103 | 104 | # Getting total number of objects in the bucket 105 | try: 106 | s3 = boto3.resource('s3') 107 | bucket = s3.Bucket(bucket_name) 108 | print bcolors.BOLD + "Bucket Scan started " +bcolors.ENDC 109 | bucket_content = bucket.objects.all() 110 | total_objects = len(list(bucket_content)) 111 | except: 112 | parser.print_help() 113 | print "Wrong bucket name or credentials in your profile ", options.aws_profile, " do not grant access to the bucket", bucket_name 114 | sys.exit(-1) 115 | 116 | if total_objects == 0: 117 | print bcolors.OKGREEN + "This bucket is empty " +bcolors.ENDC 118 | sys.exit(0) 119 | else: 120 | print "Total number of objects in the bucket: ", total_objects 121 | 122 | 123 | #initialize timer and counters 124 | timer = pb.ProgressBar(widgets=widgets, maxval=total_objects).start() 125 | i=0 126 | not_encrypted =[] 127 | 128 | # Looking for each object properties 129 | for obj in bucket_content: 130 | key = s3.Object(bucket.name, obj.key) 131 | # Amout of the objects in the bucket could increase during s3 bucket audit casuing timer going out of range 132 | if i < total_objects: 133 | timer.update(i) 134 | i+=1 135 | if key.server_side_encryption is None: 136 | not_encrypted.append(key) 137 | timer.finish() 138 | 139 | 140 | # Result summary 141 | print bcolors.WARNING +"We have found : ", len(not_encrypted), " NOT encrypted objects"+bcolors.ENDC , " out of total of ",total_objects, " objects in the Bucket" 142 | 143 | # Suggesting to save results 144 | if options.results_file is None: 145 | results_file = raw_input('Please, provide file name to save results or hit Enter to ingore: ') 146 | else: 147 | results_file = options.results_file 148 | 149 | # Saving or printing results 150 | if not results_file: 151 | for s3_nc_key in not_encrypted: 152 | print bcolors.WARNING +"Not encrypted object found: " +bcolors.ENDC, s3_nc_key 153 | sys.exit(0) 154 | else: 155 | with open(results_file, 'w+') as f: 156 | f.writelines("%s \n" % str(s3_nc_key) for s3_nc_key in not_encrypted) 157 | print "Done" 158 | -------------------------------------------------------------------------------- /selfdefence.PoC.cf/selfdefence.infosec.vpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | "Description" : "AWS Events Self-Defence PoC", 4 | "Parameters" : { 5 | "Environment" : { 6 | "Description": "Environment to deploy. You can specify staging or prod or dev", 7 | "Type": "String", 8 | "MinLength" : "2", 9 | "MaxLength" : "50" 10 | } 11 | }, 12 | "Mappings" : { 13 | "Enviroments" : { 14 | "regions" : { "staging": "us-east-1", "prod" : "us-east-1", "dev": "us-east-1", "dr": "eu-central-1" }, 15 | "SelfDefencelLambdaCodeVer" : { "staging": "S3ObjectVersionOfYourPythonLambdaFunctionZip", "prod" : "QVw4T", "dev": "jkL", "dr": "ahO" } 16 | } 17 | }, 18 | "Resources" : { 19 | "SelfDefenceLambda": { 20 | "Type": "AWS::Lambda::Function", 21 | "DependsOn": "SelfDefenceLambdaRole", 22 | "Properties": { 23 | "Handler": "selfdefence_infosec.lambda_handler", 24 | "Role": { "Fn::GetAtt" : ["SelfDefenceLambdaRole", "Arn"] }, 25 | "Code": { 26 | "S3Bucket": { "Fn::Join" : ["", ["you.bucket.name", {"Ref": "Environment"} , ".cloudform" ]]}, 27 | "S3Key": "selfdefence_infosec.zip", 28 | "S3ObjectVersion": { "Fn::FindInMap" : [ "Enviroments", "SelfDefencelLambdaCodeVer", {"Ref": "Environment"}]} 29 | }, 30 | "Runtime": "python2.7", 31 | "Timeout": "25" 32 | } 33 | }, 34 | "SelfDefenceLambdaRole": { 35 | "Type": "AWS::IAM::Role", 36 | "Properties": { 37 | "AssumeRolePolicyDocument": { 38 | "Version" : "2012-10-17", 39 | "Statement": [ { 40 | "Effect": "Allow", 41 | "Principal": { 42 | "Service": [ "lambda.amazonaws.com" ] 43 | }, 44 | "Action": [ "sts:AssumeRole" ] 45 | } ] 46 | }, 47 | "Path": "/infosec/services/", 48 | "Policies" : [{ "PolicyName": "SelfDefenceLambda", "PolicyDocument": { 49 | "Version": "2012-10-17", 50 | "Statement": [ 51 | { 52 | "Sid": "SelfDefenceLambda2016", 53 | "Effect": "Allow", 54 | "Action": [ 55 | "sns:Publish", 56 | "logs:CreateLogGroup", 57 | "logs:CreateLogStream", 58 | "logs:PutLogEvents", 59 | "iam:PutUserPolicy" 60 | ], 61 | "Resource": "*" 62 | 63 | } ] 64 | }}] 65 | } 66 | }, 67 | "SelfDefenceLambdaInvokePermission": { 68 | "Type": "AWS::Lambda::Permission", 69 | "DependsOn" : ["SelfDefenceLambda", "TerminationProtectionEventRule"], 70 | "Properties": { 71 | "FunctionName" : { "Fn::GetAtt" : ["SelfDefenceLambda", "Arn"] }, 72 | "Action": "lambda:InvokeFunction", 73 | "Principal": "events.amazonaws.com", 74 | "SourceArn": { "Fn::GetAtt" : ["TerminationProtectionEventRule", "Arn"] } 75 | } 76 | }, 77 | "TerminationProtectionEventRule": { 78 | "Type": "AWS::Events::Rule", 79 | "DependsOn" : "SelfDefenceLambda", 80 | "Properties": { 81 | "Description": "Detecting Modify instance API call to get TerminationProtection Event", 82 | "EventPattern": { 83 | "detail-type": ["AWS API Call via CloudTrail"], 84 | "detail": { 85 | "eventSource": [ 86 | "ec2.amazonaws.com" 87 | ], 88 | "eventName": [ 89 | "ModifyInstanceAttribute" 90 | ] 91 | } 92 | }, 93 | "State": "ENABLED", 94 | "Targets": [{ 95 | "Arn": { "Fn::GetAtt": ["SelfDefenceLambda", "Arn"] }, 96 | "Id": "SelfDefenceV1" 97 | }] 98 | } 99 | } 100 | 101 | 102 | 103 | 104 | 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /selfdefence.PoC.cf/selfdefence_infosec.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import json 4 | import boto3 5 | import zlib 6 | import base64 7 | 8 | def lambda_handler(event, context): 9 | print event 10 | # analyzing event 11 | if event['detail']['requestParameters'].get('disableApiTermination')!= None: 12 | protection_status = event['detail']['requestParameters']['disableApiTermination']['value'] 13 | UserName = event['detail']['userIdentity']['userName'] 14 | UserID = event['detail']['userIdentity']['principalId'] 15 | if event['detail']['userIdentity'].get('sessionContext') != None: 16 | mfa = event['detail']['userIdentity']['sessionContext']['attributes']['mfaAuthenticated'] 17 | else: 18 | mfa = "false" 19 | print protection_status, UserName, UserID, mfa 20 | # disabling user using inline user policy if no MFA being used 21 | if mfa != "true" and not protection_status: 22 | iam = boto3.resource('iam') 23 | user_policy = iam.UserPolicy(UserName,'disable_user') 24 | response = user_policy.put(PolicyDocument='{ "Version": "2012-10-17", "Statement": [{"Sid": "Disableuser01","Effect": "Deny","Action": ["ec2:StopInstances", "ec2:TerminateInstances"],"Resource": ["*"]}]}') 25 | print response 26 | 27 | 28 | # # to test execution on local computer 29 | # content = open("test_termination_protection_event.json", "r") 30 | # data = content.read() 31 | # content.close() 32 | # # loading JSON 33 | # js = json.loads(data) 34 | # 35 | # 36 | # lambda_handler(js, "test") 37 | -------------------------------------------------------------------------------- /tf/infosec.tf: -------------------------------------------------------------------------------- 1 | # Enviroment targeting 2 | 3 | variable "aws_profile" { } 4 | variable "enviroment_name" { } 5 | variable "company_name" { } 6 | variable "master_account" { } 7 | variable "region" { default = "us-east-1" } 8 | 9 | # Template names 10 | variable "path_to_cf" { default= "../cf/security.global.cf/" } 11 | variable "security_global" { default = "security.global.yaml" } 12 | variable "cloudtrail_global" { default = "cloudtrail.global.json" } 13 | variable "awsconfig_global" { default = "awsconfig.global.json" } 14 | variable "cloudtrailalarms_global" { default = "cloudtrailalarms.global.json" } 15 | variable "iam_global" { default = "iam.global.json" } 16 | 17 | 18 | variable "azs" { 19 | type = "map" 20 | default = { 21 | "0" = "c" 22 | "1" = "d" 23 | } 24 | } 25 | 26 | 27 | provider "aws" { 28 | region = "${var.region}" 29 | profile = "${var.aws_profile}" 30 | } 31 | 32 | #-------------------------------------- 33 | # Creating Security Clouformation stack 34 | #-------------------------------------- 35 | 36 | # s3 bucket for cloudformation template 37 | 38 | resource "aws_s3_bucket" "CFbucket" { 39 | bucket = "com.${var.company_name}.${var.enviroment_name}.cloudform" 40 | acl = "private" 41 | versioning { 42 | enabled = true 43 | } 44 | lifecycle_rule { 45 | id = "global" 46 | enabled = true 47 | noncurrent_version_expiration { 48 | days = 90 49 | } 50 | } 51 | } 52 | 53 | # uploading CloudFormation template to the bucket 54 | 55 | resource "aws_s3_bucket_object" "security_global" { 56 | bucket = "${aws_s3_bucket.CFbucket.bucket}" 57 | key = "${var.security_global}" 58 | source = "${var.path_to_cf}${var.security_global}" 59 | etag = "${md5(file("${var.path_to_cf}${var.security_global}"))}" 60 | } 61 | 62 | resource "aws_s3_bucket_object" "cloudtrail_global" { 63 | bucket = "${aws_s3_bucket.CFbucket.bucket}" 64 | key = "${var.cloudtrail_global}" 65 | source = "${var.path_to_cf}${var.cloudtrail_global}" 66 | etag = "${md5(file("${var.path_to_cf}${var.cloudtrail_global}"))}" 67 | } 68 | 69 | resource "aws_s3_bucket_object" "awsconfig_global" { 70 | bucket = "${aws_s3_bucket.CFbucket.bucket}" 71 | key = "${var.awsconfig_global}" 72 | source = "${var.path_to_cf}${var.awsconfig_global}" 73 | etag = "${md5(file("${var.path_to_cf}${var.awsconfig_global}"))}" 74 | } 75 | 76 | resource "aws_s3_bucket_object" "cloudtrailalarms_global" { 77 | bucket = "${aws_s3_bucket.CFbucket.bucket}" 78 | key = "${var.cloudtrailalarms_global}" 79 | source = "${var.path_to_cf}${var.cloudtrailalarms_global}" 80 | etag = "${md5(file("${var.path_to_cf}${var.cloudtrailalarms_global}"))}" 81 | } 82 | 83 | resource "aws_s3_bucket_object" "iam_global" { 84 | bucket = "${aws_s3_bucket.CFbucket.bucket}" 85 | key = "${var.iam_global}" 86 | source = "${var.path_to_cf}${var.iam_global}" 87 | etag = "${md5(file("${var.path_to_cf}${var.iam_global}"))}" 88 | } 89 | # creating Security cloudforation stack 90 | 91 | resource "aws_cloudformation_stack" "Security" { 92 | name = "Security" 93 | depends_on = ["aws_s3_bucket_object.iam_global", "aws_s3_bucket_object.cloudtrailalarms_global", "aws_s3_bucket_object.awsconfig_global", "aws_s3_bucket_object.cloudtrail_global", "aws_s3_bucket_object.security_global"] 94 | parameters { 95 | AccountNickname = "${var.enviroment_name}", 96 | CompanyName = "${var.company_name}", 97 | MasterAccount = "${var.master_account}" 98 | } 99 | template_url = "https://s3.amazonaws.com/${aws_s3_bucket.CFbucket.bucket}/${var.security_global}?versionId=${aws_s3_bucket_object.security_global.version_id}" 100 | capabilities = [ "CAPABILITY_NAMED_IAM" ] 101 | tags { "owner" = "infosec"} 102 | } 103 | 104 | # --------------------------- 105 | # Configuring Account Level Password Policy 106 | #---------------------------- 107 | 108 | resource "aws_iam_account_password_policy" "strict" { 109 | minimum_password_length = 14 110 | require_lowercase_characters = true 111 | require_numbers = true 112 | require_uppercase_characters = true 113 | require_symbols = true 114 | allow_users_to_change_password = true 115 | max_password_age = 90 116 | password_reuse_prevention = 24 117 | } 118 | 119 | 120 | # --------------------------- 121 | # Configuring Users, policy, keys 122 | #---------------------------- 123 | 124 | 125 | 126 | # --------------------------- 127 | # Creating git repos for InfraAsCode 128 | #---------------------------- 129 | 130 | 131 | 132 | # --------------------------- 133 | # Building VPCs and Networks 134 | #---------------------------- 135 | 136 | 137 | #-------------------------- 138 | # Seting-up Route53 DNS 139 | #-------------------------- 140 | 141 | 142 | 143 | #-------------------------- 144 | # Seting-up security groups 145 | #-------------------------- 146 | 147 | 148 | 149 | #----------------------- 150 | # Building the bastion host 151 | #----------------------- 152 | 153 | 154 | 155 | 156 | 157 | #----------------------- 158 | # IAM Roles 159 | #----------------------- 160 | -------------------------------------------------------------------------------- /tf/terraform.tfvars: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # General 3 | #-------------------------------------------------------------- 4 | #Replace it-secuity and master_account # with corresponding to your comapny data 5 | aws_profile = "it-security" 6 | enviroment_name = "prod" 7 | company_name = "it-security" 8 | region = "us-east-1" 9 | # Master account for AWS cross account access (using ITOrganizationAccountAccessRole with MFA enforced) 10 | master_account = "1234566789" 11 | 12 | #-------------------------------------------------------------- 13 | # Network 14 | #-------------------------------------------------------------- 15 | # Office 16 | # Please specify you office external IP range (for future use) 17 | rp_cidr = "172.0.0.1/24" 18 | 19 | # VPC 20 | vpc_cidr = "" 21 | 22 | # Instances Subnets 23 | private_subnets = "" 24 | public_subnets = "" 25 | protected_subnets = "" 26 | 27 | 28 | #-------------------------------------------------------------- 29 | # Instances 30 | #-------------------------------------------------------------- 31 | 32 | # CentOS 6 (x86_64) - with Updates HVM 33 | # https://aws.amazon.com/marketplace/pp/B00NQAYLWO?qid=1489159620460&sr=0-2&ref_=srh_res_product_title 34 | centos_6_ami.ca-central-1 = "ami-b17cced5" # Canada (Central) 35 | centos_6_ami.us-east-1 = "ami-1c221e76" # US East (N. Virginia) 36 | centos_6_ami.us-east-2 = "ami-c299c2a7" # US East (Ohio) 37 | centos_6_ami.us-west-1 = "ami-ac5f2fcc" # US West (N. California) 38 | centos_6_ami.us-west-2 = "ami-05cf2265" # US West (Oregon) 39 | centos_6_ami.eu-central-1 = "ami-2bf11444" # EU (Frankfurt) 40 | centos_6_ami.eu-west-1 = "ami-edb9069e" # EU (Ireland) 41 | centos_6_ami.eu-west-2 = "ami-ba373dde" # EU (London) 42 | centos_6_ami.ap-southeast-1 = "ami-106aa373" # Asia Pacific (Singapore) 43 | centos_6_ami.ap-southeast-2 = "ami-87d2f4e4" # Asia Pacific (Sydney) 44 | centos_6_ami.ap-northeast-1 = "ami-fa3d3f94" # Asia Pacific (Tokyo) 45 | centos_6_ami.ap-northeast-2 = "ami-56478938" # Asia Pacific (Seoul) 46 | centos_6_ami.ap-south-1 = "ami-9b1c76f4" # Asia Pacific (Mumbai) 47 | centos_6_ami.sa-east-1 = "ami-03b93b6f" # South America (Sao Paulo) 48 | --------------------------------------------------------------------------------