Serverless stack security guidelines
What is a stack?
A stack is a collection of serverless functions, services, and database which are defined within a cloudformation stack in accordance to the cloudformation docs.
What is a serverless stack?
A serverless stack in the definition of villa market is where a stack does not contain any service that needs to be managed manually eg EC2, virtual machine, or Kubernetes-like services.
Features of a good serverless stack
a secure serverless stacks should have the following features
- permission should be defined within the template.yml file
- there should be no hard coded passwords/secret key in the stacks
- every function/services should have resource-based-permission set up within the template
- the stacks should not contain hard-coded resource names
- if connection to external stacks/service is required, all the secret keys should be stored using secret-manager or equivalent
- stack should be auto-deployed by an automated ci/cd tool, all the required environment variable should be stored securely within the tool
Example of a serverless stack design
Example of a good serverless app which contains a managed service
Design diagram
yaml cloudformation template docs
yaml docs should define all resources including functions and databases without hardcoding the resource names.
Resources:
getMember:
Type: AWS::Serverless::Function
Properties:
CodeUri: getSetData
Handler: app.getMember
Runtime: python3.8
FunctionName: !Join [ "-" , [ !Ref 'AWS::StackName', "get-member"]]
Policies:
- DynamoDBWritePolicy:
TableName: !Join [ "-" , ["member", "*", !Ref BRANCH]]
- DynamoDBReadPolicy:
TableName: !Join [ "-" , ["member", "*", !Ref BRANCH]]
MemberDatabase:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: hashedPhone
AttributeType: S
- AttributeName: memberId
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: hashedPhone
KeyType: HASH
- AttributeName: memberId
KeyType: RANGE
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
TableName: !Join [ "-" , ["member", "database", !Ref BRANCH]]
Setting up Policies
AWS allows policies to be specified using the Policies:
tag
Policies:
# use pre-defined sam policy template
- DynamoDBWritePolicy:
TableName: !Join [ "-" , ["member", "*", !Ref BRANCH]]
- DynamoDBReadPolicy:
TableName: !Join [ "-" , ["member", "*", !Ref BRANCH]]
# manually defined policy
- Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'cloudformation:Describe*'
- 'cloudformation:List*'
- 'cloudformation:Get*'
Resource: '*'
The users are able to set policies using pre-defined templated provided by the sam toolkit or define them manually using cloudfront guidelines
Resource based policy should be set up so that the entity is only callable by the specified function/iam entity. There are many tools to help with generating these including awsPolicyGenerator or reading this documentation