|
#!/bin/bash |
|
|
|
# automate deployment of slack notifier described shown below |
|
# https://aws.amazon.com/blogs/security/enabling-aws-security-hub-integration-with-aws-chatbot/ |
|
|
|
# usage: |
|
# $0 SlackWorkSpaceID SlackChannelID [SlackWebhookURL] |
|
|
|
# prerequisite |
|
# obtain apropreate IDs beforehand |
|
# be sure value of AWS_DEFAULT_REGION which one, as well as credentials like shown below |
|
# export AWS_ACCESS_KEY_ID="XXXXXXXXXXXXXXXXXXXX" |
|
# export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
# export AWS_SESSION_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
|
|
# if SlackWebhookURL was given, following solution would additionaly be 'create-stack'ed |
|
# https://aws.amazon.com/blogs/apn/how-to-enable-custom-actions-in-aws-security-hub/ |
|
|
|
# Worked: |
|
# Ubuntu |
|
# aws-cli/1.16.206 Python/3.5.2 Linux/4.10.0-38-generic botocore/1.15.44 |
|
|
|
# Dependencies: |
|
# bc |
|
|
|
# There is no point to go further if negative |
|
aws securityhub describe-hub || exit 1 |
|
|
|
echo "attempting on $AWS_DEFAULT_REGION" >&2 |
|
|
|
# Examine if configuration for AWS Chatbot which is a global service is already configured |
|
# if something like below found, assumes affirmative |
|
# 'arn:aws:iam::111111111111:role/SecurityHubToAWSChatBot-ChatBotManageIAMRole-XXXXXXXXXXXX' |
|
# Since I could not find any api which provicde directly access for AWS Chatbot |
|
# So unfortunately tracing any belongings of the configuration like an iam role above |
|
function might_SlackChannelConfig_configured_previously() { |
|
local some=$(aws iam list-roles \ |
|
--query 'Roles[?contains(RoleName,`ChatBotManageIAMRole`)].{name:RoleName}' \ |
|
--output text) |
|
if [ -n "$some" ]; then |
|
return 0 |
|
else |
|
return 1 |
|
fi |
|
} |
|
|
|
|
|
if might_SlackChannelConfig_configured_previously; then |
|
readonly configureSlackChannel='false' |
|
else |
|
readonly configureSlackChannel='true' |
|
fi |
|
|
|
echo "configureSlackChannel=$configureSlackChannel" >&2 |
|
|
|
function get_stack() { |
|
aws cloudformation describe-stacks \ |
|
--stack-name $1 \ |
|
--output text |
|
} |
|
|
|
#Wait CREATE_COMPLETE in exponential backoff |
|
function await_completion() { |
|
local statusis=`mktemp` |
|
trap "[ -f $statusis ] && rm $statusis" ERR EXIT |
|
seq 0 10 | xargs -I{} echo "2^{}-1" | bc | while read standby; do |
|
sleep "`shuf -i 0-$standby -n 1`" |
|
|
|
local state=$(aws cloudformation describe-stacks \ |
|
--stack-name $1 \ |
|
--query 'Stacks[].{StackName:StackName,StackStatus:StackStatus}' \ |
|
--output text) |
|
if echo $state | grep 'IN_PROGRESS'; then |
|
: |
|
else |
|
printf '0' > $statusis && break |
|
fi |
|
#[ -n "`get_stack $1 | grep ''`" ] && printf '0' > $statusis && break |
|
printf '1' > $statusis |
|
echo 'Stack is still not yet ready' >&2 |
|
done |
|
[ "`cat $statusis`" = '1' ] && echo "[ERROR] Gave up. Something is wrong with the stack $1" >&2 |
|
return "`cat $statusis`" |
|
} |
|
|
|
function does_exists() { |
|
aws cloudformation describe-stacks \ |
|
--stack-name $1 \ |
|
--query 'Stacks[?StackName==`'$1'`]' > /dev/null |
|
} |
|
|
|
function get_sns_topic() { |
|
local arn=$(aws sns list-topics \ |
|
--query 'Topics[?contains(TopicArn,`SecurityHubToAWSChatBot-SNSTopicAWSChatBot`)].{arn:TopicArn}' \ |
|
--output text) |
|
|
|
echo $arn |
|
|
|
if [ -n "$arn" ]; then |
|
return 0 |
|
else |
|
return 1 |
|
fi |
|
} |
|
|
|
function is_target_defined() { |
|
local target=$(aws events list-targets-by-rule --rule $1 --query 'Targets[?Id==`'$1'`].{Id:Id}' --output text) |
|
|
|
if [ -n "$target" ]; then |
|
return 0 |
|
else |
|
return 1 |
|
fi |
|
} |
|
|
|
|
|
# Unnecessary for stack_2. stack_2 does not depend on this |
|
# attempt only if webhookurl was given |
|
if [ -n "$3" ]; then |
|
readonly stack_1='EnableSecurityHubFindingsToSlack' |
|
|
|
if does_exists $stack_1; then |
|
aws cloudformation update-stack \ |
|
--stack-name $stack_1 \ |
|
--template-body "`curl https://raw.githubusercontent.com/aws-samples/aws-securityhub-to-slack/master/SecurityHubFindingsToSlack.json`" \ |
|
--parameters '[{"ParameterKey":"IncomingWebHookURL","ParameterValue":"'$3'"},{"ParameterKey":"SlackChannel","ParameterValue":"'$2'"}]' \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
else |
|
aws cloudformation create-stack \ |
|
--stack-name $stack_1 \ |
|
--template-body "`curl https://raw.githubusercontent.com/aws-samples/aws-securityhub-to-slack/master/SecurityHubFindingsToSlack.json`" \ |
|
--parameters '[{"ParameterKey":"IncomingWebHookURL","ParameterValue":"'$3'"},{"ParameterKey":"SlackChannel","ParameterValue":"'$2'"}]' \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
await_completion $stack_1 |
|
fi |
|
fi |
|
|
|
readonly chatbotbody='ChatBotBodySecurityHubToSlack' |
|
readonly chatbotbody_template="`curl https://raw.githubusercontent.com/o2346/aws-securityhub-to-slack/edge/ChatbotBody.yml`" |
|
#readonly chatbotbody_template="`cat ~/Documents/aws-securityhub-to-slack/ChatbotBody.yml`" |
|
#readonly chatbotbody_template="`curl https://raw.githubusercontent.com/aws-samples/aws-securityhub-to-slack/master/ChatbotBody.yml`" |
|
|
|
if [ "$configureSlackChannel" = 'true' ]; then |
|
if does_exists $chatbotbody; then |
|
echo updating |
|
aws cloudformation update-stack \ |
|
--stack-name $chatbotbody \ |
|
--template-body "$chatbotbody_template" \ |
|
--parameters '[{"ParameterKey":"SlackWorkSpaceID","ParameterValue":"'$1'"},{"ParameterKey":"SlackChannelID","ParameterValue":"'$2'"}]' \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
await_completion $chatbotbody |
|
else |
|
echo creating |
|
aws cloudformation create-stack \ |
|
--stack-name $chatbotbody \ |
|
--template-body "$chatbotbody_template" \ |
|
--parameters '[{"ParameterKey":"SlackWorkSpaceID","ParameterValue":"'$1'"},{"ParameterKey":"SlackChannelID","ParameterValue":"'$2'"}]' \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
await_completion $chatbotbody |
|
fi |
|
fi |
|
|
|
readonly stack_2='SecurityHubToAWSChatBot' |
|
readonly stack_2_template="`curl https://raw.githubusercontent.com/o2346/aws-securityhub-to-slack/edge/SecurityHub_to_AWSChatBot.yml`" |
|
#readonly stack_2_template="`cat ~/Documents/aws-securityhub-to-slack/SecurityHub_to_AWSChatBot.yml`" |
|
#readonly stack_2_template="`curl https://raw.githubusercontent.com/aws-samples/aws-securityhub-to-slack/master/SecurityHub_to_AWSChatBot.yml`" |
|
|
|
if does_exists $stack_2; then |
|
echo updating |
|
aws cloudformation update-stack \ |
|
--stack-name $stack_2 \ |
|
--template-body "$stack_2_template" \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
await_completion $stack_2 |
|
else |
|
echo creating |
|
aws cloudformation create-stack \ |
|
--stack-name $stack_2 \ |
|
--template-body "$stack_2_template" \ |
|
--capabilities CAPABILITY_NAMED_IAM |
|
await_completion $stack_2 |
|
fi |
|
#https://github.com/boozallen/devsecops-example-helloworld/issues/1 |
|
|
|
readonly rule='All_SecurityHub_Findings_to_Slack' |
|
|
|
if aws events describe-rule --name $rule > /dev/null; then |
|
aws events list-targets-by-rule --rule $rule --query 'Targets[?Id==`'$rule'`].{Id:Id}' --output text | xargs -I{} aws events remove-targets --rule $rule --ids {} |
|
aws events delete-rule --name $rule |
|
fi |
|
aws events put-rule --name $rule \ |
|
--event-pattern '{ |
|
"detail-type": ["Security Hub Findings - Imported"], |
|
"source": ["aws.securityhub"], |
|
"detail": { |
|
"findings": { |
|
"Severity": { |
|
"Label": [ |
|
"CRITICAL" |
|
] |
|
}, |
|
"Compliance": { |
|
"Status": [ |
|
"FAILED" |
|
] |
|
}, |
|
"Workflow": { |
|
"Status": [ "NEW" ] |
|
}, |
|
"RecordState": [ "ACTIVE" ] |
|
} |
|
} |
|
}' |
|
# Modify labels as your need |
|
# "HIGH", |
|
# "MEDIUM" |
|
|
|
readonly topicarn="`get_sns_topic`" |
|
aws events put-targets --rule $rule --targets "Id"="$rule","Arn"="$topicarn" |
|
|
|
# memo |
|
# would be better to support style like |
|
# $0 -w SlackWorkSpaceID -c SlackChannelID -u SlackWebhookURL |
|
|
|
|
|
|