Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(kms): Allow Functions to Decrypt Environment Variables #1

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/internals/generated_resources.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,31 @@ AWS::IAM::Role CodeDeployServiceRole

NOTE: ``AWS::IAM::Role`` resources are only generated if no Role parameter is supplied for DeploymentPreference


With KmsKeyArn Property
~~~~~~~~~~~~~~~~~~~~~~~

Example:

.. code:: yaml

MyFunction:
Type: AWS::Serverless::Function
Properties:
...
KmsKeyArn: !GetAtt MyKmsKey.Arn
...


Additional generated resources:

================================== ================================
CloudFormation Resource Type Logical ID
================================== ================================
AWS::IAM::Poilicy MyFunction\ **DecryptEnvironmentVariablesPolicy**
================================== ================================


With Events
~~~~~~~~~~~

Expand Down
35 changes: 35 additions & 0 deletions samtranslator/model/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ class IAMRole(Resource):
runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")}


class IAMPolicy(Resource):
resource_type = "AWS::IAM::Policy"
property_types = {
"PolicyDocument": PropertyType(True, is_type(object)),
"PolicyName": PropertyType(True, is_str()),
"Roles": PropertyType(True, is_type(list)),
}

runtime_attrs = {"name": lambda self: ref(self.logical_id)}


class IAMRolePolicies:
@classmethod
def construct_assume_role_policy_for_service_principal(cls, service_principal):
Expand Down Expand Up @@ -129,3 +140,27 @@ def lambda_invoke_function_role_policy(cls, function_arn, logical_id):
},
}
return document


class IAMPolicies:
@classmethod
def lambda_decrypt_environment_variables_policy(cls, logical_id, kms_key_arn, function_arn, role_arn):
policy_name = "DecryptEnvironmentVariablesPolicy"
policy = IAMPolicy(logical_id + policy_name)
policy.PolicyName = policy_name
policy.PolicyDocument = {
"Version": "2012-10-17",
"Statement": [
{
"Action": "kms:Decrypt",
"Effect": "Allow",
"Resource": kms_key_arn,
"Condition": {
"StringEquals": {"kms:ViaService": "lambda.amazonaws.com"},
"ForAnyValue:ArnEquals": {"kms:EncryptionContext:aws:lambda:FunctionArn": function_arn},
},
}
],
}
policy.Roles = [role_arn]
return policy
18 changes: 17 additions & 1 deletion samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from samtranslator.model.dynamodb import DynamoDBTable
from samtranslator.model.exceptions import InvalidEventException, InvalidResourceException
from samtranslator.model.resource_policies import ResourcePolicies, PolicyTypes
from samtranslator.model.iam import IAMRole, IAMRolePolicies
from samtranslator.model.iam import IAMRole, IAMPolicy, IAMRolePolicies, IAMPolicies
from samtranslator.model.lambda_ import (
LambdaFunction,
LambdaVersion,
Expand Down Expand Up @@ -183,6 +183,10 @@ def to_cloudformation(self, **kwargs):
lambda_function.Role = execution_role.get_runtime_attr("arn")
resources.append(execution_role)

if self.KmsKeyArn:
env_vars_policy = self._construct_environment_variables_decrypt_policy(lambda_function)
resources.append(env_vars_policy)

try:
resources += self._generate_event_resources(
lambda_function,
Expand Down Expand Up @@ -493,6 +497,18 @@ def _construct_role(self, managed_policy_map, event_invoke_policies):
)
return execution_role

def _construct_environment_variables_decrypt_policy(self, lambda_function):
"""Constructs a Lambda policy for reading encrypted environment variables
based on this SAM function's KmsKeyArn property.

:returns: the generated IAM Policy
:rtype: model.iam.IAMPolicy
"""
policy = IAMPolicies.lambda_decrypt_environment_variables_policy(
self.logical_id, lambda_function.KmsKeyArn, lambda_function.get_runtime_attr("arn"), lambda_function.Role
)
return policy

def _validate_package_type(self, lambda_function):
"""
Validates Function based on the existence of Package type
Expand Down
206 changes: 141 additions & 65 deletions tests/translator/output/aws-cn/function_with_kmskeyarn.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
{
"Resources": {
"FunctionWithKeyArnRole": {
"Type": "AWS::IAM::Role",
"myKey": {
"Type": "AWS::KMS::Key",
"Properties": {
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Description": "A sample key",
"KeyPolicy": {
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Allow administration of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/Alice"
},
"Action": [
"kms:Create*"
],
"Resource": "*"
}
]
}
}
},
"FunctionWithKeyArn": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Handler": "hello.handler",
"Role": {
"Fn::GetAtt": [
"FunctionWithKeyArnRole",
"Arn"
]
},
"Runtime": "python2.7",
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
"Key": "lambda:createdBy",
"Value": "SAM"
}
],
"KmsKeyArn": "thisIsaKey"
}
},
"FunctionWithKeyArnRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
Expand All @@ -27,111 +65,149 @@
}
}
]
}
}
},
"FunctionWithReferenceToKeyArnRole": {
"Type": "AWS::IAM::Role",
"Properties": {
},
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
"Key": "lambda:createdBy",
"Value": "SAM"
}
],
"AssumeRolePolicyDocument": {
]
}
},
"FunctionWithKeyArnDecryptEnvironmentVariablesPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Action": "kms:Decrypt",
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
"Resource": "thisIsaKey",
"Condition": {
"StringEquals": {
"kms:ViaService": "lambda.amazonaws.com"
},
"ForAnyValue:ArnEquals": {
"kms:EncryptionContext:aws:lambda:FunctionArn": {
"Fn::GetAtt": [
"FunctionWithKeyArn",
"Arn"
]
}
}
}
}
]
}
},
"PolicyName": "DecryptEnvironmentVariablesPolicy",
"Roles": [
{
"Fn::GetAtt": [
"FunctionWithKeyArnRole",
"Arn"
]
}
]
}
},
"FunctionWithKeyArn": {
"FunctionWithReferenceToKeyArn": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
}
],
"KmsKeyArn": "thisIsaKey",
"Handler": "hello.handler",
"Role": {
"Fn::GetAtt": [
"FunctionWithKeyArnRole",
"FunctionWithReferenceToKeyArnRole",
"Arn"
]
},
"Runtime": "python2.7"
"Runtime": "python2.7",
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
],
"KmsKeyArn": {
"Ref": "myKey"
}
}
},
"myKey": {
"Type": "AWS::KMS::Key",
"FunctionWithReferenceToKeyArnRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"KeyPolicy": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Action": [
"kms:Create*"
"sts:AssumeRole"
],
"Sid": "Allow administration of the key",
"Resource": "*",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/Alice"
"Service": [
"lambda.amazonaws.com"
]
}
}
]
},
"Description": "A sample key"
"ManagedPolicyArns": [
"arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Tags": [
{
"Key": "lambda:createdBy",
"Value": "SAM"
}
]
}
},
"FunctionWithReferenceToKeyArn": {
"Type": "AWS::Lambda::Function",
"FunctionWithReferenceToKeyArnDecryptEnvironmentVariablesPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "kms:Decrypt",
"Effect": "Allow",
"Resource": {
"Ref": "myKey"
},
"Condition": {
"StringEquals": {
"kms:ViaService": "lambda.amazonaws.com"
},
"ForAnyValue:ArnEquals": {
"kms:EncryptionContext:aws:lambda:FunctionArn": {
"Fn::GetAtt": [
"FunctionWithReferenceToKeyArn",
"Arn"
]
}
}
}
}
]
},
"Tags": [
"PolicyName": "DecryptEnvironmentVariablesPolicy",
"Roles": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
"Fn::GetAtt": [
"FunctionWithReferenceToKeyArnRole",
"Arn"
]
}
],
"KmsKeyArn": {
"Ref": "myKey"
},
"Handler": "hello.handler",
"Role": {
"Fn::GetAtt": [
"FunctionWithReferenceToKeyArnRole",
"Arn"
]
},
"Runtime": "python2.7"
]
}
}
}
}
}