Cloudformation update stack policy condition on stack name or environment - amazon-web-services

I have a policy attached to my CloudFormation stack:
{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"Update:*"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Principal": "*",
"Resource": "*",
"Action": [
"Update:Replace",
"Update:Delete"
],
"Condition": {
"StringEquals": {
"ResourceType": [
"AWS::SNS::Topic",
"AWS::SQS::Queue"
]
}
}
}
]
}
The policy prevents accidental deleting of SNS/SQS resources. I want to make the policy more liberal in a dev environment. How do I disable the Deny statement conditionally, for example, if my CF (cloudformation) stack name is my-app-dev or the CF stack has a tag STAGE equal to dev?
Btw the policy is generated by the serverless framework, so I will have to write it in serverless.yml

This can be done by using the environment variables of the serverless framework.
serverless.yml
service: sample
provider:
name: aws
stage: ${opt:stage,"dev"}
region: ap-northeast-1
custom:
policyChange:
prd: Deny
dev: Allow
resources:
- ${file(iam.yml)}
iam.yml
Resources:
SampleRole:
Type: AWS::IAM::Role
Properties:
RoleName: SampleRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: SamplePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: '${self:custom.policyChange.${self:provider.stage}}'
Resource: "*"
Action:
- sqs:*

Related

AWS Service-linked role for DynamoDB tables and global secondary indexes autoscaling

As per the documentation here, there is a service linked role for dynamodb auto scaling - AWSServiceRoleForApplicationAutoScaling_DynamoDBTable.
The role permissions policy allows Application Auto Scaling to complete the following actions on all resources:
Action: dynamodb:DescribeTable
Action: dynamodb:UpdateTable
Action: cloudwatch:DeleteAlarms
Action: cloudwatch:DescribeAlarms
Action: cloudwatch:PutMetricAlarm
which translates to (from here),
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:UpdateTable",
"cloudwatch:DeleteAlarms",
"cloudwatch:DescribeAlarms",
"cloudwatch:PutMetricAlarm"
],
"Resource": "*"
}
]
}
So for example, when the policy is used like below,
TableLiveProductsReadScalableTarget:
Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
Properties:
MaxCapacity: !Ref TableLiveProductsReadMaxCap
MinCapacity: !Ref TableLiveProductsReadMinCap
ResourceId: !Sub "table/${TableLiveProducts}"
RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable
ScalableDimension: 'dynamodb:table:ReadCapacityUnits'
ServiceNamespace: dynamodb
from a security perspective is it okay to assume that, since the role can only be assumed by dynamodb.application-autoscaling.amazonaws.com no problem giving permission to update ALL the tables, delete ALL the alarms etc.?
What is the rationale behind asking for such a wildcard permission here (as well as in many AWS built service linked roles)?
They are meant to be most generic for your account. So one role covers every table for you. Since principle is dynamodb.application-autoscaling.amazonaws.com no other service nor IAM user/role can use these permissions.
You can provide your own role instead, with more granual setup. So to limit permissions to only one table you can do:
MyDynamoDBRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: {'Service': ['dynamodb.application-autoscaling.amazonaws.com']}
Action: ['sts:AssumeRole']
Path: '/'
Policies:
- PolicyName: DynamoDBScaling
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:UpdateTable"
],
"Resource": "${TableLiveProducts.Arn}"
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricAlarm",
"cloudwatch:DescribeAlarms",
"cloudwatch:DeleteAlarms"
],
"Resource": "*"
}
]
}
TableLiveProductsReadScalableTarget:
Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
Properties:
MaxCapacity: !Ref TableLiveProductsReadMaxCap
MinCapacity: !Ref TableLiveProductsReadMinCap
ResourceId: !Sub "table/${TableLiveProducts}"
RoleARN: !GetAtt MyDynamoDBRole.Arn
ScalableDimension: 'dynamodb:table:ReadCapacityUnits'
ServiceNamespace: dynamodb

Using AWS::Region in Policy Resources

I am trying to dynamically change the s3 resource name based on current cloud formation stack region. Cloudformation stack updates without any error.
Am I doing something wrong? I am expecting to have a policy with {AWS::Region} resolved to us-east-1.
Version: 2012-10-17
Statement:
- Sid: RestrictS3Access
Effect: Allow
Action:
- 's3:GetObject'
Resource:
- !Sub "arn:aws:s3:::dnsa-${AWS::Region}test/${cognito-identity.amazonaws.com:sub}"
- !Sub "arn:aws:s3:::dnsa-${AWS::Region}test/${cognito-identity.amazonaws.com:sub}/*"
I am expecting to see the policy as follows. I am checking results from aws console.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::dnsa-us-east-1/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::dnsa-us-east-1/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow",
"Sid": "RestrictS3Access"
}
If you want ${cognito-identity.amazonaws.com:sub} to remain unchanged, you need to escape it with ${!}.
Version: 2012-10-17
Statement:
- Sid: RestrictS3Access
Effect: Allow
Action:
- 's3:GetObject'
Resource:
- !Sub "arn:aws:s3:::dnsa-${AWS::Region}test/${!cognito-identity.amazonaws.com:sub}"
- !Sub "arn:aws:s3:::dnsa-${AWS::Region}test/${!cognito-identity.amazonaws.com:sub}/*"

S3 Bucket Policy to Allow access to specific AWS services and users and restrict other all

I have a bucket policy which is restricting other users to access. But I want, For aws services it should be accessible like EMR etc.
I found same question is asked here:
S3 Bucket Policy to Allow access to specific users and restrict all . But I want to add services also. Like aws services can access that bucket but not users.
This is my bucket policy:
AUser:
Description: Name of the AUser
Type: String
BUser:
Description: Name of the BUser
Type: String
MetadataBucket:
Description: Name of the Metadata Bucket
Type: String
Resources:
MetadataBucketSecurity:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: MetadataBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Deny
NotPrincipal:
AWS:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${AUser}'
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${BUser}'
Action:
- 's3:ListBucket'
- 's3:RestoreObject'
- 's3:ReplicateObject'
- 's3:PutObject'
- 's3:PutBucketNotification'
- 's3:PutBucketLogging'
- 's3:PutObjectTagging'
- 's3:DeleteObject'
- 's3:GetObjectAcl'
- 's3:GetObject'
- 's3:GetBucketLogging'
- 's3:GetBucketAcl'
- 's3:ListBucketByTags'
- 's3:GetObjectVersionAcl'
- 's3:GetBucketPolicy'
Resource:
- !Sub 'arn:aws:s3:::${Bucket}'
- !Sub 'arn:aws:s3:::${Bucket}/*'
I tried to add this services directly, but it did not worked.
AUser:
Description: Name of the AUser
Type: String
BUser:
Description: Name of the BUser
Type: String
MetadataBucket:
Description: Name of the Metadata Bucket
Type: String
Resources:
MetadataBucketSecurity:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: MetadataBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Deny
NotPrincipal:
AWS:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${AUser}'
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${BUser}'
Service:
- 'elasticmapreduce.amazonaws.com'
- 'ec2.amazonaws.com'
Action:
- 's3:ListBucket'
- 's3:RestoreObject'
- 's3:ReplicateObject'
- 's3:PutObject'
- 's3:PutBucketNotification'
- 's3:PutBucketLogging'
- 's3:PutObjectTagging'
- 's3:DeleteObject'
- 's3:GetObjectAcl'
- 's3:GetObject'
- 's3:GetBucketLogging'
- 's3:GetBucketAcl'
- 's3:ListBucketByTags'
- 's3:GetObjectVersionAcl'
- 's3:GetBucketPolicy'
Resource:
- !Sub 'arn:aws:s3:::${Bucket}'
- !Sub 'arn:aws:s3:::${Bucket}/*'
After that I tried this also:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::ano:root",
"arn:aws:iam::ano:user/AUser",
]
},
"Action": [
"s3:ListBucket",
"s3:RestoreObject",
"s3:ReplicateObject",
"s3:PutBucketNotification",
"s3:PutBucketLogging",
"s3:PutObjectTagging",
"s3:DeleteObject",
"s3:GetObjectAcl",
"s3:GetBucketLogging",
"s3:GetBucketAcl",
"s3:ListBucketByTags",
"s3:GetObjectVersionAcl",
"s3:GetBucketPolicy"
],
"Resource": [
"arn:aws:s3:::Bucket1",
"arn:aws:s3:::Bucket1/*"
]
},
{
"Sid": "InventoryAndAnalyticsExamplePolicy",
"Effect": "Allow",
"Principal": {
"Service": [
"elasticmapreduce.amazonaws.com",
"ec2.amazonaws.com",
"s3.amazonaws.com"
]
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::Bucket1",
"arn:aws:s3:::Bucket1/*"
]
}
]
}
But still it is not working
Is there any way where I can give access of S3 buckets to particular users and restrict others and with that AWS services should be access to that bucket?

ECS cluster cannot use KMS key to decrypt "you are not allowed to access"

I continue to get the error:
software.amazon.awssdk.services.kms.model.KmsException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.
When attempting to decrypt.
I've created a Task execution role with the permission:
"AssumeRolePolicyDocument": {
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
],
"Policies": [
{
"PolicyName": "AllowKmsDecrypt",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
{"Ref": "PrincipalSourceKeyArn"}
]
}
]
}
}
]
And the task definition is associated with the role:
"ExecutionRoleArn": {"Ref": "TaskExecutionRoleArn"},
Hm. What else could i be missing?
From these docs it turns out an IAM policy is not sufficient:
IAM policies by themselves are not sufficient to allow access to a
CMK. However, you can use them in combination with a CMK's key policy
if the key policy enables it. Giving the AWS account full access to
the CMK does this; it enables you to use IAM policies to give IAM
users and roles in the account access to the CMK
I needed to update the KMS KeyPolicy to include:
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": { "Fn::Join" : ["" , ["arn:aws:iam::", {"Ref" : "AWS::AccountId"} ,":root" ]] }
},
"Action": "kms:*",
"Resource": "*"
}
In case of this line
{ "Fn::Join" : ["" , ["arn:aws:iam::", {"Ref" : "AWS::AccountId"} ,":root" ]] }
you are allowing to use this key by root account only.
Usually, this key policy has to provide manage possibilities for users and only a few specific actions for services or other users that use this key. So as for me the whole set up has to look something like this:
KMSKeyEncryption:
Type: AWS::KMS::Key
Properties:
Enabled: true
EnableKeyRotation: false
KeyPolicy:
Version: 2012-10-17
Statement:
- Principal:
AWS:arn of the users/roles who are allowed to manage this key
Effect: Allow
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
- kms:Encrypt*
- kms:Decrypt*
Resource: "*"
- Principal: "*" # this is not specific enough, should be strict
Effect: Allow
Action:
- kms:Decrypt*
Resource: "*"
PolicyDecryptKms:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: DecryptKmsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowDecryptValues
Effect: Allow
Action:
- kms:Decrypt*
Resource: !GetAtt KMSKeyEncryption.Arn
RoleECSTaskContainer:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2008-10-17
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
RoleName: ECSTaskContainerRole
ManagedPolicyArns:
- !Ref PolicyDecryptKms

AWS S3 Bucket policies

I have created a bucket with cloudformation:
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
BucketName:
Type: String
Description: "Choose a name for the S3 Bucket"
Default: "myrandomnameforbucket"
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
AccessControl: "Private"
BucketName: !Ref BucketName
Now I'm writing a bucketPolicy but I'm facing some issues. What I want to achieve:
USER A (UserA) can upload to S3
USER A (UserA) can NOT DELETE from S3
ALL users (in my environment, not public) can read from S3
ALL users (in my environment, nob public) can delete from S3
How can I achieve this?
At the moment I denied a delete from userA and allowed an upload from userA.
- Effect: Deny
Principal:
AWS:
!GetAtt UserA.Arn
Action: "s3:DeleteObject"
Resource:
Fn::Join: ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
- Effect: Allow
Principal:
AWS:
!GetAtt UserA.Arn
Action: "s3:PutObject"
Resource:
Fn::Join: ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
- Effect: Allow
Principal: "?" # * is public?
Action: s3:GetObject
Resource:
Fn::Join: ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
I understand there are two questions:
how to grant access to all IAM users when excluding anonymous users
how to restrict one user more than the others, that is: removing rights you just granted to others
The first question sounds easy at first, as the documentation states:
In resource-based policies, use the Principal element to specify the accounts or users who are allowed to access the resource
So this would mean you could do something like:
Principal:
AWS: !Ref "AWS::AccountId"
But when I tried it just didn't work. When setting the arn of a specific user it worked for me. This seems like a bug to me. Or an unclarity in the documentation. There is this other report I found.
Anyway, what you can do is to use Principal: AWS: "*" and then use a Condition to restrict to IAM users only.
The second question is much easier: policies are evaluated such that explicit denys have priority over general allows, see documentation.
The resulting policy can be e.g. written like this:
S3Policy:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Statement:
- Effect: Deny
Action: "s3:DeleteObject"
Resource: !Join ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
Principal:
AWS: !GetAtt UserA.Arn
- Effect: Allow
Action: "s3:PutObject"
Resource: !Join ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
Principal:
AWS: !GetAtt UserA.Arn
- Effect: Allow
Action: ["s3:GetObject", "s3:DeleteObject"]
Resource: !Join ["", ["arn:aws:s3:::", Ref: "S3Bucket", "/*"]]
Principal:
AWS: "*"
Condition:
StringEquals:
"aws:PrincipalType": ["User"]
{
"Version": "2012-10-17",
"Id": "S3PolicyId1",
"Statement": [
{
"Sid": "AllowGet",
"Effect": "Allow",
"Principal":{"AWS":"arn:aws:iam::account-number-without-hyphens:user/user1"},
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::s3_bucket_name",
"arn:aws:s3:::s3_bucket_name/*"
]
},
{
"Sid": "DenyDeleteObject",
"Effect": "Deny",
"Principal": {"AWS":"arn:aws:iam::account-number-without-hyphens:user/user1"},
"Action": "s3:Delete*",
"Resource": [
"arn:aws:s3:::s3_bucket_name",
"arn:aws:s3:::s3_bucket_name/*"
]
},
{
"Sid": "Allow anyone in your account to access bucket",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-number-without-hyphens:root"
},
"Action": [
"s3:Get*",
"s3:List*",
"s3:Put*",
"s3:Delete*"
],
"Resource": [
"arn:aws:s3:::s3_bucket_name",
"arn:aws:s3:::s3_bucket_name/*"
]
}
]
}
Here is a template in JSON format I quickly put together. My assumption here is that your group "ALL users (in my environment, nob public)" is everybody in the account. Hence, we define it in the third block. You can always manipulate the principal with whatever you want.
If you have any question please ask, I am happy to help.