aws security - an engineer’s introduction to aws security auditing using cis and the cli
TRANSCRIPT
Caveat:
I’m an Engineer, not a developer.
I script, I don’t code, this won’t be pretty...
Goals of this presentation
▷ Introduction
▷Quick overview of CIS
▷Quick overview of security and AWS
▷Pass on some lessons learned
▷Provide some CLI examples
▷Save you some time and pain
▷Recommendations based on these
So why write your own tool?
Besides CLI Naming inconsistencies…
Besides being a good way to
learn AWS Security…
▷ I didn’t want to have to go to the web
interface or a document every time I wanted
to do a security review
▷AWS has Trusted Advisor, but charges for
more than basic checks
▷Although the CLI has quite good help, the
naming and use of tags and switches is
frustratingly inconsistent
▷No source (including me) is perfect, more
than one source of verification is good
I already had a Security
Auditing tool of my own… [1]
▷Supported a number of UNIX OS,
including Amazon Linux
▷Used the CIS Benchmarks already
▷Was free (apart from my time) and
required minimal additional software
▷Had a number of people using it, so
would get some additional testing
▷ I could add additional tests as I
discovered new security
recommendations and tips[1] https://github.com/lateralblast/lunar
Security BenchmarksWhy choose the CIS Benchmark?
It’s good to have a common
point of reference as a start…
▷Used by a lot of people and places as a
basis for their own security processes
▷Has a long track record
▷Well trusted, and has industry support
▷Mentioned on AWS Security Blog [1]
▷Semi regularly updated
▷Has explanation and implementation
notes as well as the standard checkbox
▷Has GUI and CLI remediation steps[1] https://aws.amazon.com/blogs/security/tag/cis-aws-foundations-benchmark/
But it’s only a start…
▷ It’s not perfect (e.g. typos in remediation)
▷Not everything is going to be applicable to
your organisation / application
▷You’ll have your own processes on top of it
▷Sometimes lags with updates
▷ It’s always good to have more than one
source of verification, especially for security
▷ It’s a paper document, needs to be
automated to reduce work and human error
Security FundamentalsAn Engineers attempt to explain security…
Traditional:
Security in layers
Network, Application, OS, Users, etc.
Least access / privilege by default
Restrict access to privileged
accounts
Monitor usage of privileged accounts
Use Multifactor Authentication
Enable password complexity
Enable password / credential rotation
Create roles and add users to them
Enable and manage logging
Generate alerts
Encrypt at rest and in transit
What is old is new again…
AWS:
Security in layers
Network, Application, OS, Users, etc.
Least access / privilege by default
Restrict access to “root” account
Monitor use of IAM
Use Multifactor Authentication
Enable password complexity
Enable password / credential rotation
Create roles and add users to them
Enable and manage logging
Generate alerts
Encrypt at rest and in transit
AWS CLI Security AuditingAn Engineers attempt to audit via the CLI…
What does this involve?
▷An overview of key areas:
▷ IAM (Users, Groups, Roles, Policies, MFA)
▷Monitoring (Logging, Metrics, Alerting)
▷Encryption (at rest and in flight)
▷Networking (VPCs and Security Groups)
▷Some CLI examples of how to get and set
security parameters where appropriate
AWS CLI Security AuditingIAM (Users, Groups, Roles, Policies, MFA)
Avoid use of the “root” account
▷Attach IAM policies to groups and roles and
use them to delegate responsibility to
management accounts [1]
▷Minimise use of “root” account to those
functions that require it e.g. requesting a
penetration test of creating a CloudFront
private key
[1] http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
IAM Account Security (MFAs etc.)
▷Ensure MFA is enabled for “root” account
▷Ensure MFA is enabled for other IAM
users
▷Consider hardware MFA for ”root” account
▷Use MFA devices where applicable and
lock the device away in the case of the
root user
▷Delegate management of MFA devices [1]
▷Hardware, Virtual and SMS based MFA [2][1] https://aws.amazon.com/blogs/security/how-to-delegate-management-of-multi-factor-authentication-to-aws-iam-users/
[2] http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa.html
Checking MFAs
$ aws iam generate-credential-report
{
"State": "STARTED",
"Description": "No report exists. Starting a new report generation task”
}
$ aws iam get-credential-report --query 'Content' --output text |base64 –D \
|cut -d, f1,4,8
user,password_enabled,mfa_active
<root_account>,not_supported,true
spindler,false,false
$ aws iam list-virtual-mfa-devices –-query “VirtualMFADevices”
[
{
"SerialNumber": "arn:aws:iam::123456789012:mfa/ExampleMFADevice”
}
]
$ aws iam get-account-summary | grep "AccountMFAEnabled”
"AccountMFAEnabled": 1,
Managing Credentials
▷Manage Access and Secret keys used for
programmatic access via SDK and HTTP [1]
▷Ensure credentials unused for 90 days or
greater are removed
▷Ensure keys are rotated every 90 days or
less
▷Done manually, or better programmatically
▷Only create keys for users that need them,
and do not have keys for “root” account [2][1] http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html
[2] http://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html
Checking Credentials
$ aws iam generate-credential-report
{
"State": "STARTED",
"Description": "No report exists. Starting a new report generation task”
}
$ aws iam get-credential-report --query 'Content' --output text |base64 –D \
|cut -d, -f1,4,9,11,14,16 |grep -v '<root_account>’
user,password_enabled,access_key_1_active,access_key_1_last_used_date, \
access_key_2_active,access_key_2_last_used_date
spindler,false,true,2017-01-22T00:11:00+00:00,false,N/A
$ aws iam list-access-keys --user-name spindler --query \
"AccessKeyMetadata[].{AccessKeyId:AccessKeyId, Status:Status}” \
[
{
"Status": "Active",
"AccessKeyId": "AKIAISKTDTHXSGFO5ZFQ”
}
]
$ aws iam delete-access-key --access-key AKIAISKTDTHXSGFO5ZFQ –-user-name spindler
IAM Password Policies
▷At least one uppercase letter
▷At least one lowercase letter
▷At least one symbol
▷At least one number
▷Minimum length of 14
▷Prevent password reuse
▷Expires within 90 days
Getting and Setting Password Policies
$ aws iam get-account-password-policy
{
"PasswordPolicy": {
"AllowUsersToChangePassword": true,
"RequireLowercaseCharacters": true,
"RequireUppercaseCharacters": true,
"MinimumPasswordLength": 14,
"RequireNumbers": true,
"RequireSymbols": true,
"ExpirePasswords": true
}
}
$ aws iam update-account-password-policy --require-uppercase-characters
$ aws iam update-account-password-policy --require-lowercase-characters
$ aws iam update-account-password-policy --require-symbols
$ aws iam update-account-password-policy --require-numbers
$ aws iam update-account-password-policy --minimum-password-length 14
$ aws iam update-account-password-policy --password-reuse-prevention 24
$ aws iam update-account-password-policy --max-password-age 90
IAM Policies
▷ Ensure IAM policies are attached only to
groups or roles [1] [2]
▷Ensure IAM Master and Manager roles are
active (like RBAC, use with EC2 and
Lambda)
▷Ensure IAM instance roles are used for AWS
resource access for instances [3][4]
▷Ensure there are no policies that allow full
“*:*” administrative privileges[1] http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
[2] http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html
[3] http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html
[4] http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon- ec2.html
IAM Policies
$ aws iam list-users --query 'Users[*].UserName' --output text
$ aws iam list-attached-user-policies --user-name <iam_user>
$ aws iam list-user-policies --user-name <iam_user>
$ aws iam list-policies --output text |awk '{print $2","$5}' \
|grep -v "arn:aws:iam::aws:policy”
arn:aws:iam::XXXXXXXXXXXX:policy/cloudformationcreatestack,v2
arn:aws:iam::XXXXXXXXXXXX:policy/IAM-Manager,v1
$ aws iam get-policy-version --policy-arn <arn> --version <version> \
--query "PolicyVersion.Document.Statement[?Effect == 'Allow' && \
contains(Resource, '*') && contains (Action, '*')]”
$ aws iam list-entities-for-policy --policy-arn <arn>
$ aws iam detach-role-policy --role-name <role> --policy-arn <arn>
Interfacing with AWS Support
▷Consider enabling security questions for
AWS support calls
▷Maintain security and current contact details
▷Ensure a support role has been created to
manage incidents with AWS support
▷Support does not allow you to allow or deny
access to individual actions so assign
allowing access to all cases, so assign
appropriately
Interfacing with AWS Support
$ aws iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess']”
[
{
"PolicyName": "AWSSupportAccess",
"CreateDate": "2015-02-06T18:41:11Z",
"AttachmentCount": 0,
"IsAttachable": true,
"PolicyId": "ANPAJSNKQX2OW67GF4S7E",
"DefaultVersionId": "v1",
"Path": "/",
"Arn": "arn:aws:iam::aws:policy/AWSSupportAccess",
"UpdateDate": "2015-02-06T18:41:11Z”
}
]
AWS CLI Security AuditingLogging (and some Log Management)
Logging
▷Ensure CloudTrail is enabled in all regions
▷Ensure CloudTrail log file validation is
enabled
▷Ensure the S3 bucket CloudTrail logs to is
not publicly accessible
▷Ensure CloudTrail trails are integrated with
CloudWatch Logs
▷Ensure VPC flow logging is enabled in all
VPCs [1][1] http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/flow-logs.html
Logging
$ aws cloudtrail describe-trails --query "trailList[].IsMultiRegionTrail" --output text
True
$ aws cloudtrail create-trail --name <trail_name> --bucket-name \
<s3_bucket_for_cloudtrail> --is-multi-region-trail
$ aws cloudtrail update-trail --name <trail_name> --is-multi-region-trail
$ aws cloudtrail describe-trails --query "trailList[].LogFileValidationEnabled” --output text
True
$ aws cloudtrail update-trail --name <trail_name> --enable-log-file-validation
$ aws cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text
$ aws s3api get-bucket-acl --bucket <bucket_name> |grep URI |grep AllUsers
$ aws cloudtrail describe-trails --query "trailList[].CloudWatchLogsLogGroupArn" --output text
$ aws cloudtrail get-trail-status --name <trail_name>
$ aws ec2 describe-flow-logs --query FlowLogs[].FlowLogId --output text
$ aws ec2 describe-flow-logs --query FlowLogs[].ResourceId --output text
$ aws ec2 describe-flow-logs --filter "Name=resource-id,Values=<vpc>" |grep FlowLogStatus
Log and Key Management
▷Ensure S3 bucket access logging is enabled
on the CloudTrail S3 bucket
▷Adjust log retention according to
requirements [1]
▷Ensure AWS Config is enabled in all regions
▷Consider encrypting CloudTrail logs at rest
using KMS and ensure keys are rotated [2]
[1] http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/SettingLogRetention.html
[2] https://docs.aws.amazon.com/awscloudtrail/latest/userguide/encrypting-cloudtrail-log-files-with-aws-kms.html
Log and Key Management
$ aws s3api get-bucket-logging --bucket <s3_bucket_for_cloudtrail>
$ aws configservice describe-configuration-recorders
$ aws cloudtrail describe-trails |grep KmsKeyId
$ aws cloudtrail update-trail --name <trail_name> --kms-id <cloudtrail_kms_key> \
aws kms put-key-policy --key-id <cloudtrail_kms_key> \
--policy <cloudtrail_kms_key_policy>
$ aws kms list-keys
$ aws kms get-key-rotation-status --key-id <kms_key_id>
AWS CLI Security AuditingIAM Monitoring (Logging, Metrics, Alerting)
IAM Monitoring
▷Unauthorized API calls
▷Management Console sign-in without MFA
▷Usage of "root" account
▷ IAM policy changes
▷AWS Management Console authentication
failures
▷Set thresholds accordingly [1]
[1] http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/viewing_metrics_with_cloudwatch.html
IAM Monitoring – Unauthorised API Calls
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\") }”
$ aws cloudwatch describe-alarms –-query \
'MetricAlarms[?MetricName==`<unauthorized_api_calls_metric>`]'
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <unauthorized_api_calls_metric> --metric-transformations \
metricName=<unauthorized_api_calls_metric>,metricNamespace='Audit',\
metricVal ue=1 --filter-pattern '{ ($.errorCode = "*UnauthorizedOperation") \
|| ($.errorCode = "AccessDenied*") }'
$ aws sns create-topic --name <sns_topic_name>
$ aws sns subscribe --topic-arn <sns_topic_arn> --protocol <protocol_for_sns> \
-- notification-endpoint <sns_subscription_endpoints>
$ aws cloudwatch put-metric-alarm --alarm-name <unauthorized_api_calls_alarm> \
--metric-name <unauthorized_api_calls_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – Login Without MFA
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = "ConsoleLogin") && ($.additionalEventData.MFAUsed
!= "Yes") }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<no_mfa_console_signin_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <no_mfa_console_signin_metric> --metric-transformations \
metricName=<no_mfa_console_signin_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = "ConsoleLogin") && \
($.additionalEventData.MFAUsed != "Yes") }’
$ aws sns create-topic --name <sns_topic_name>
$ aws sns subscribe --topic-arn <sns_topic_arn> --protocol <protocol_for_sns> \
-- notification-endpoint <sns_subscription_endpoints>
$ aws cloudwatch put-metric-alarm --alarm-name <no_mfa_console_signin_alarm> \
--metric-name <no_mfa_console_signin_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – “root” Account Usage
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ $.userIdentity.type = \"Root\" && $.userIdentity.invokedBy
NOT EXISTS && $.eventType != \"AwsServiceEvent\" } ”
$ aws cloudwatch describe-alarms --query 'MetricAlarms[?MetricName==`<root_usage_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <root_usage_metric> --metric-transformations \
metricName=<root_usage_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS \
&& $.eventType != "AwsServiceEvent" }’
$ aws sns create-topic --name <sns_topic_name>
$ aws sns subscribe --topic-arn <sns_topic_arn> --protocol <protocol_for_sns> \
-- notification-endpoint <sns_subscription_endpoints>
$ aws cloudwatch put-metric-alarm --alarm-name <root_usage_alarm> \
--metric-name <root_usage_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' -- alarm-actions <sns_topic_arn>
IAM Monitoring – IAM Policy Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern":
"{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=Delete
UserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=P
utUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=Cr
eatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)|
|($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUs
erPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}"
$ aws cloudwatch describe-alarms --query 'MetricAlarms[?MetricName==`<iam_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <iam_changes_metric> --metric-transformations \
metricName=<iam_changes_metric>,metricNamespace='Audit',metricValue=1
--filter-pattern '{($.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)}'
IAM Monitoring – Authentication Failures
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed
authentication\") }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<console_signin_failure_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <console_signin_failure_metric> --metric-transformations \
metricName=<console_signin_failure_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = ConsoleLogin) && \
($.errorMessage = ""Failed authentication"") }’
$ aws sns create-topic --name <sns_topic_name>
$ aws sns subscribe --topic-arn <sns_topic_arn> --protocol <protocol_for_sns> \
-- notification-endpoint <sns_subscription_endpoints>
$ aws cloudwatch put-metric-alarm --alarm-name <console_signin_failure_alarm> \
--metric-name <console_signin_failure_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
AWS CLI Security AuditingConfig Monitoring (Logging, Metrics, Alerting)
CloudTrail, Config, S3, and Key
Monitoring
▷CloudTrail configuration changes
▷AWS Config configuration changes
▷S3 bucket policy changes
▷Disabling or scheduled deletion of customer
created CMKs
▷Set thresholds accordingly [1]
[1] http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/viewing_metrics_with_cloudwatch.html
Monitoring – CloudTrail Config Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) ||
($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName =
StopLogging) }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<cloudtrail_cfg_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <cloudtrail_cfg_changes_metric> --metric-transformations \
metricName=<cloudtrail_cfg_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) \
|| ($.eventName = DeleteTrail) || ($.eventName = StartLogging) \
|| ($.eventName = StopLogging) }'
$ aws cloudwatch put-metric-alarm --alarm-name <cloudtrail_cfg_changes_alarm> \
--metric-name <cloudtrail_cfg_changes_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
Monitoring – AWS Config Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{($.eventSource = config.amazonaws.com) &&
(($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel)
||($.eventName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<aws_config_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <aws_config_changes_metric> --metric-transformations \
metricName=<aws_config_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{($.eventSource = config.amazonaws.com) && \
(($.eventName=StopConfigurationRecorder)||($.eventName=DeleteDeliveryChannel) \
||($.even tName=PutDeliveryChannel)||($.eventName=PutConfigurationRecorder))}’
$ aws cloudwatch put-metric-alarm --alarm-name <aws_config_changes_alarm> \
--metric-name <aws_config_changes_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
Monitoring – S3 Bucket Policy Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventSource = s3.amazonaws.com) && (($.eventName =
PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) ||
($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) ||
($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName
= DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<s3_bucket_policy_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <s3_bucket_policy_changes_metric> --metric-transformations \
metricName=<s3_bucket_policy_changes_metric>,metricNamespace='Audit',metric Value=1 \
--filter-pattern '{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl)\
|| ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) \
|| ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) \
|| ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) \
|| ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }’
$ aws cloudwatch put-metric-alarm --alarm-name <s3_bucket_policy_changes_alarm> \
--metric-name <s3_bucket_policy_changes_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
Monitoring – Customer Created CMKs
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{($.eventSource = kms.amazonaws.com) &&
(($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion))} }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<disable_or_delete_cmk_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <disable_or_delete_cmk_metric> --metric-transformations \
metricName=<disable_or_delete_cmk_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{($.eventSource = kms.amazonaws.com) && \
(($.eventName=DisableKey)||($.eventName=ScheduleKeyDeletion))}’
$ aws cloudwatch put-metric-alarm --alarm-name <disable_or_delete_cmk_alarm> \
--metric-name <disable_or_delete_cmk_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
-- namespace 'Audit' --alarm-actions <sns_topic_arn>
AWS CLI Security AuditingVPC Monitoring (Logging, Metrics, Alerting)
Security Group and VPC Monitoring
▷Security Group changes
▷NACL changes
▷Network Gateway changes
▷Route changes
▷VPC changes
▷SNS subscribers
▷Set thresholds accordingly [1]
[1] http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/viewing_metrics_with_cloudwatch.html
IAM Monitoring – Security Group Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName =
AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) ||
($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) ||
($.eventName = DeleteSecurityGroup)}”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<security_group_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <security_group_changes_metric> --metric-transformations \
metricName=<security_group_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = AuthorizeSecurityGroupIngress)\
|| ($.eventName = AuthorizeSecurityGroupEgress) \
|| ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) \
|| ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup)}'
$ aws cloudwatch put-metric-alarm --alarm-name <security_group_changes_alarm> \
--metric-name <security_group_changes_metric> --statistic Sum --period 300 \
--threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – NACL Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = CreateNetworkAcl) || ($.eventName =
CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName =
DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName =
ReplaceNetworkAclAssociation) }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<nacl_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <nacl_changes_metric> --metric-transformations \
metricName=<nacl_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = CreateNetworkAcl) \
|| ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) \
|| ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) \
|| ($.eventName = ReplaceNetworkAclAssociation) }’
$ aws cloudwatch put-metric-alarm --alarm-name <nacl_changes_alarm> \
--metric-name <nacl_changes_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – Gateway Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = CreateCustomerGateway) || ($.eventName =
DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName =
CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName =
DetachInternetGateway) }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<network_gw_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <network_gw_changes_metric> --metric-transformations \
metricName=<network_gw_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = CreateCustomerGateway)\
|| ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway)\
|| ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway)\
|| ($.eventName = DetachInternetGateway) }’
$ aws cloudwatch put-metric-alarm --alarm-name <network_gw_changes_alarm> \
--metric-name <network_gw_changes_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – Route Table Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) ||
($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) ||
($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName =
DisassociateRouteTable) }”
$ aws cloudwatch describe-alarms \
--query 'MetricAlarms[?MetricName==`<route_table_changes_metric>`]’
$ aws sns list-subscriptions-by-topic --topic-arn <sns_topic_arn>
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <route_table_changes_metric> --metric-transformations \
metricName=<route_table_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) \
|| ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) \
|| ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) \
|| ($.eventName = DisassociateRouteTable) }’
$ aws cloudwatch put-metric-alarm --alarm-name <route_table_changes_alarm> \
--metric-name <route_table_changes_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
IAM Monitoring – VPC Changes
$ aws logs describe-metric-filters --log-group-name <cloudtrail_log_group_name>
"filterPattern": "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) ||
($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) ||
($.eventName = CreateVpcPeeringConnection) || ($.eventName =
DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) ||
($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) ||
($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }”
$ aws cloudwatch describe-alarms --query 'MetricAlarms[?MetricName==`<vpc_changes_metric>`]’
$ aws logs put-metric-filter --log-group-name <cloudtrail_log_group_name> \
--filter-name <vpc_changes_metric> --metric-transformations \
metricName=<vpc_changes_metric>,metricNamespace='Audit',metricValue=1 \
--filter-pattern '{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) \
|| ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) \
|| ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection)\
|| ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) \
|| ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) \
|| ($.eventName = EnableVpcClassicLink) }’
$ aws cloudwatch put-metric-alarm --alarm-name <vpc_changes_alarm> \
--metric-name <vpc_changes_metric> --statistic Sum --period 300 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold --evaluation-periods 1 \
--namespace 'Audit' --alarm-actions <sns_topic_arn>
Monitoring – SNS subscribers
$ aws sns list-topics
$ aws sns list-subscriptions-by-topic --topic-arn <topic_arn>
AWS CLI Security AuditingNetworking (VPCs and Security Groups)
Networking and Security Groups
▷Ensure SSH is not open to the world
▷Ensure RDP is not open to the world
▷Ensure the default security group of every
VPC restricts all traffic [1]
▷Ensure routing tables for VPC peering are
"least access” [2]
[1] http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html
[2] http://docs.aws.amazon.com/AmazonVPC/latest/PeeringGuide/peering- configurations-partial-access.html
Networking and Security Groups
$ aws ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=22" "Name=ip-
permission.cidr,Values=0.0.0.0/0"$
$ aws ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=3389" "Name=ip-
permission.cidr,Values=0.0.0.0/0”
$ aws ec2 describe-security-groups --filters Name=group-name,Values='default' \
--query 'SecurityGroups[].{IpPermissions:IpPermissions,GroupId:GroupId}’
$ aws ec2 describe-security-groups --filters Name=group-name,Values='default' \
--query 'SecurityGroups[].{IpPermissionsEgress:IpPermissionsEgress \
,GroupId:GroupId}’
$ aws ec2 describe-route-tables --query \
"RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes,\
AssociatedSubnets:Associations[*].SubnetId}" |grep GatewayID |grep pcx-
Thanks for your patience