Move IAM user creation into it's own CloudFormation stack

This will make the IAM user and API keys durable across API
stack rebuilds.
This also grants the IAM user rights to invoke all deployments
of the API and all SQS queues used by the API
This commit is contained in:
Gene Wood 2019-12-30 17:00:57 -08:00
Родитель 8bb843cb29
Коммит 55ac67caaf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: F0A9E7DCD39E452E
4 изменённых файлов: 118 добавлений и 62 удалений

Просмотреть файл

@ -6,6 +6,9 @@ A Slack bot that facilitates triaging MozDef alerts by automating outreach to Mo
### Deployment
To deploy the slack-triage-bot-api into AWS
* create the IAM user that MozDef will use to interact with the Lambda function
and SQS queue
`make deploy-mozdef-slack-triage-bot-user`
* determine the Slack Client Secret
* Run the make command for the environment you want
@ -88,4 +91,24 @@ which will return a value like
```json
{"result": "https://sqs.us-west-2.amazonaws.com/012345678901/MozDefSlackTraigeBotAPI-SlackTriageBotMozDefQueue-ABCDEFGHIJKL"}
```
### Discovering the Lambda function name
Call the [lambda:ListFunctions](https://docs.aws.amazon.com/lambda/latest/dg/API_ListFunctions.html)
API and filter the results based on the name. You can see an example of this by
running the make command
```shell script
make discover-lambda-function-name
```
which will return a value like `MozDefSlackTriageBotAPI-SlackTriageBotApiFunction-1N9KLDX1926F3`
### Fetching User API Keys
You can fetch the User API keys from the CloudFormation outputs
```shell script
make show-user-credentials
```

Просмотреть файл

@ -1,4 +1,5 @@
STACK_NAME := MozDefSlackTraigeBotAPI
API_STACK_NAME := MozDefSlackTriageBotAPI
USER_STACK_NAME := MozDefSlackTriageBotUser
CODE_STORAGE_S3_PREFIX := mozdef-slack-traige-bot-api
PROD_LAMBDA_CODE_STORAGE_S3_BUCKET_NAME := public.us-west-2.infosec.mozilla.org
DEV_LAMBDA_CODE_STORAGE_S3_BUCKET_NAME := public.us-west-2.security.allizom.org
@ -15,13 +16,18 @@ DEV_SLACK_CLIENT_ID := 371351187216.856548004901
# PROD_SLACK_CLIENT_SECRET := ???
# DEV_SLACK_CLIENT_SECRET := ???
.PHONE: deploy-mozdef-slack-triage-bot-user
deploy-mozdef-slack-triage-bot-user:
aws cloudformation deploy --template-file slack-triage-bot-user.yaml --stack-name $(USER_STACK_NAME) \
--capabilities CAPABILITY_IAM
.PHONE: deploy-mozdef-slack-triage-bot-api-dev
deploy-mozdef-slack-triage-bot-api-dev:
./deploy.sh \
$(DEV_ACCOUNT_ID) \
slack-triage-bot-api.yaml \
$(DEV_LAMBDA_CODE_STORAGE_S3_BUCKET_NAME) \
$(STACK_NAME) \
$(API_STACK_NAME) \
$(CODE_STORAGE_S3_PREFIX) \
"CustomDomainName=$(DEV_DOMAIN_NAME) \
DomainNameZone=$(DEV_DOMAIN_ZONE) \
@ -36,7 +42,7 @@ deploy-mozdef-slack-triage-bot-api:
$(PROD_ACCOUNT_ID) \
slack-triage-bot-api.yaml \
$(PROD_LAMBDA_CODE_STORAGE_S3_BUCKET_NAME) \
$(STACK_NAME) \
$(API_STACK_NAME) \
$(CODE_STORAGE_S3_PREFIX) \
"CustomDomainName=$(PROD_DOMAIN_NAME) \
DomainNameZone=$(PROD_DOMAIN_ZONE) \
@ -47,14 +53,14 @@ deploy-mozdef-slack-triage-bot-api:
.PHONE: test-mozdef-slack-triage-bot-api-http
test-mozdef-slack-triage-bot-api-http:
URL="`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotApiUrl'].OutputValue" --output text`test" && \
URL="`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotApiUrl'].OutputValue" --output text`test" && \
curl $$URL
.PHONE: test-mozdef-slack-triage-bot-api-invoke
test-mozdef-slack-triage-bot-api-invoke:
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda invoke \
--function-name $$FUNCTION_NAME \
--payload '{"identifier": "9Zo02m4B7gIfixq3c4Xh", "alert": "duo_bypass_codes_generated", "identityConfidence": "highest", "summary": "DUO bypass codes have been generated for your account. ", "user": "$(EMAIL_ADDRESS)"}' \
@ -64,9 +70,9 @@ test-mozdef-slack-triage-bot-api-invoke:
.PHONE: test-mozdef-slack-triage-bot-api-invoke-ssh1
test-mozdef-slack-triage-bot-api-invoke-ssh1:
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda invoke \
--function-name $$FUNCTION_NAME \
--payload '{"identifier": "9Zo02m4B7gIfixq3c4Xh", "alert": "sensitive_host_session", "identityConfidence": "low", "summary": "An SSH session to a potentially sensitive host sensitive.example.com was made by your user account.", "user": "$(EMAIL_ADDRESS)"}' \
@ -76,9 +82,9 @@ test-mozdef-slack-triage-bot-api-invoke-ssh1:
.PHONE: test-mozdef-slack-triage-bot-api-invoke-ssh2
test-mozdef-slack-triage-bot-api-invoke-ssh2:
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda invoke \
--function-name $$FUNCTION_NAME \
--payload '{"identifier": "9Zo02m4B7gIfixq3c4Xh", "alert": "ssh_access_sign_releng", "identityConfidence": "low", "summary": "An SSH session was established to host host.example.com by your user account.", "user": "$(EMAIL_ADDRESS)"}' \
@ -88,9 +94,9 @@ test-mozdef-slack-triage-bot-api-invoke-ssh2:
.PHONE: test-mozdef-slack-triage-bot-api-invoke-duo-code-used
test-mozdef-slack-triage-bot-api-invoke-duo-code-used:
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda invoke \
--function-name $$FUNCTION_NAME \
--payload '{"identifier": "9Zo02m4B7gIfixq3c4Xh", "alert": "duo_bypass_codes_used", "identityConfidence": "highest", "summary": "DUO bypass codes belonging to your account have been used to ", "user": "$(EMAIL_ADDRESS)"}' \
@ -98,11 +104,23 @@ test-mozdef-slack-triage-bot-api-invoke-duo-code-used:
cat response.json && \
rm response.json
.PHONE: show-user-credentials
show-user-credentials:
aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text && \
aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text
.PHONE: discover-lambda-function-name
discover-lambda-function-name:
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda list-functions \
--query "Functions[?contains(FunctionName, '-SlackTriageBotApiFunction-')].FunctionName" --output text
.PHONE: discover-sqs-queue-url
discover-sqs-queue-url:
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
FUNCTION_NAME=`aws cloudformation describe-stacks --stack-name $(API_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotFunctionName'].OutputValue" --output text` && \
ACCESS_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerAccessKeyId'].OutputValue" --output text` && \
SECRET_KEY=`aws cloudformation describe-stacks --stack-name $(USER_STACK_NAME) --query "Stacks[0].Outputs[?OutputKey=='SlackTriageBotInvokerSecretAccessKey'].OutputValue" --output text` && \
AWS_ACCESS_KEY_ID=$$ACCESS_KEY AWS_SECRET_ACCESS_KEY=$$SECRET_KEY AWS_SESSION_TOKEN= aws lambda invoke \
--function-name $$FUNCTION_NAME \
--payload '{"action": "discover-sqs-queue-url"}' \

Просмотреть файл

@ -266,40 +266,6 @@ Resources:
Value: !Ref AWS::StackName
- Key: source
Value: https://github.com/mozilla/MozDef-Triage-Bot
SlackTriageBotInvokerUser:
Type: AWS::IAM::User
Properties:
Policies:
- PolicyName: AllowInvokingSlackTriageBotAPI
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !GetAtt SlackTriageBotApiFunction.Arn
- PolicyName: AllowReceiveSlackTriageBotSQSQueue
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sqs:ChangeMessageVisibility
- sqs:DeleteMessage
- sqs:GetQueueAttributes
- sqs:GetQueueUrl
- sqs:ListQueueTags
- sqs:PurgeQueue
- sqs:ReceiveMessage
Resource:
- !GetAtt SlackTriageBotMozDefQueue.Arn
SlackTriageBotInvokerAccessKey:
Type: AWS::IAM::AccessKey
Properties:
Serial: 20191127
Status: Active
UserName: !Ref SlackTriageBotInvokerUser
Outputs:
SlackTriageBotApiUrl:
Description: The URL of the AWS Federated RP
@ -311,15 +277,6 @@ Outputs:
SlackTriageBotFunctionName:
Description: The AWS Lambda function name
Value: !Ref SlackTriageBotApiFunction
SlackTriageBotInvokerName:
Description: The Username of the SlackTriageBotInvoker
Value: !Ref SlackTriageBotInvokerUser
SlackTriageBotInvokerAccessKeyId:
Description: The AWS API Access Key ID of the SlackTriageBotInvoker
Value: !Ref SlackTriageBotInvokerAccessKey
SlackTriageBotInvokerSecretAccessKey:
Description: The AWS API Access Key Secret Key of the SlackTriageBotInvoker
Value: !GetAtt SlackTriageBotInvokerAccessKey.SecretAccessKey
SlackTriageBotMozDefSQSQueueArn:
Description: The ARN of the MozDef SQS Queue
Value: !GetAtt SlackTriageBotMozDefQueue.Arn

Просмотреть файл

@ -0,0 +1,58 @@
AWSTemplateFormatVersion: 2010-09-09
Description: MozDef user used to invoke the Slack Triage Bot Lambda function and fetch messages from the SQS queue
Metadata:
Source: https://github.com/mozilla/MozDef-Triage-Bot/blob/master/cloudformation/slack-triage-bot-user.yaml
Resources:
SlackTriageBotInvokerUser:
Type: AWS::IAM::User
Properties:
Policies:
- PolicyName: AllowInvokingSlackTriageBotAPI
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Join [ ':', [ 'arn:aws:lambda', !Ref 'AWS::Region', !Ref 'AWS::AccountId', 'function:*-SlackTriageBotApiFunction-*' ] ]
- PolicyName: AllowListLambdaFunctions
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:ListFunctions
Resource:
- '*'
- PolicyName: AllowReceiveSlackTriageBotSQSQueue
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sqs:ChangeMessageVisibility
- sqs:DeleteMessage
- sqs:GetQueueAttributes
- sqs:GetQueueUrl
- sqs:ListQueueTags
- sqs:PurgeQueue
- sqs:ReceiveMessage
Resource:
- !Join [ ':', [ 'arn:aws:sqs', !Ref 'AWS::Region', !Ref 'AWS::AccountId', '*-SlackTriageBotMozDefQueue-*' ] ]
SlackTriageBotInvokerAccessKey:
Type: AWS::IAM::AccessKey
Properties:
Serial: 20191230
Status: Active
UserName: !Ref SlackTriageBotInvokerUser
Outputs:
SlackTriageBotInvokerName:
Description: The Username of the SlackTriageBotInvoker
Value: !Ref SlackTriageBotInvokerUser
SlackTriageBotInvokerAccessKeyId:
Description: The AWS API Access Key ID of the SlackTriageBotInvoker
Value: !Ref SlackTriageBotInvokerAccessKey
SlackTriageBotInvokerSecretAccessKey:
Description: The AWS API Access Key Secret Key of the SlackTriageBotInvoker
Value: !GetAtt SlackTriageBotInvokerAccessKey.SecretAccessKey