Edit AWS Cognito Identity Pool using serverless.yml - amazon-web-services

I am configuring an AWS Cognito Identity Pool using the severless framework, and I am editing a file in the yml configuration to add an unauthenticated role for users to upload an image to an s3 bucket.
The code was previously deployed without an unauthenticated role being specified, and the deployment went fine and was stable. After I went looking for a way to control the permissions regarding accessing the S3 bucket, I discovered that the only way to give write, but not read, permissions on an S3 bucket is to specify it in a user policy, so I have to add an unathenticated role to the identity pool. However, when I deploy the code, I get an error stating:
Serverless Error ---------------------------------------
An error occurred: CognitoIdentityPoolRoles - Resource cannot be updated.
I have managed to get around the problem in the dev environment but it required totally deleting the stack and rebuilding it from scratch.
I also do not want to go in and adjust the resources manually in the AWS console since resources should be managed in cloudformation or in the console, but doing it both ways leads to chaos.
So, at the moment, the options I see are to delete the entire stack and rebuild it with the new roles, or find a way to update through cloudformation.
Does anyone have a way to avoid the first option and allow me to update the stack without attaching the role in the console?
Relevant section of serverless.yml is below...
Resources:
# The federated identity for our user pool to auth with
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
# Generate a name based on the stage
IdentityPoolName: ${self:custom.stage}MyIdentityPool
# Allow unathenticated users
AllowUnauthenticatedIdentities: true
# Link to our User Pool
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt: [ "CognitoUserPool", "ProviderName" ]
# IAM roles
CognitoIdentityPoolRoles:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: CognitoIdentityPool
Roles:
authenticated:
Fn::GetAtt: [CognitoAuthRole, Arn]
# Next two lines are the 2 lines of code which break everything
unauthenticated:
Fn::GetAtt: [CognitoUnAuthRole, Arn]
# IAM role for UN-authenticated users
CognitoUnAuthRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Federated: 'cognito-identity.amazonaws.com'
Action:
- 'sts:AssumeRoleWithWebIdentity'
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud':
Ref: CognitoIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': unauthenticated
Policies:
- PolicyName: 'CognitoUnAuthorizedPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'mobileanalytics:PutEvents'
- 'cognito-sync:*'
- 'cognito-identity:*'
Resource: '*'
# Allow users to upload attachments to their
# folder inside our S3 bucket
- Effect: 'Allow'
Action:
- 's3:PutObject'
Resource:
- Fn::Join:
- ''
-
- Fn::GetAtt: [MediafilesBucket, Arn]
- '/submissions/'

Fixed.
I commented out the sections of serverless.yml related to the identity pool deployed (destroyed), then uncommented that section, redeployed, and restored from backup.
It seems to be a bit of a hack, but it worked.
I also feel like there should be a way to edit identity pool roles through cloudformation...

Related

aws lambda permission to IAM usergroup using cloudformation

I need create a way for execute especific lambdas for specific IAM users, then i am doing:
I am creating a Usergroup in cloudformation:
Resources:
XXXGroup:
Type: AWS::IAM::Group
And after I am creating a policy and add the policy to my UserGroup
UsersXPolicies:
Type: AWS::IAM::Policy
Properties:
Groups:
- !Ref XXXGroup
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "lambda:*"
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunction
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunction2
PolicyName: xxx-access-policy
I try first only with "lambda:InvokeFunction" but it not works, then I try with lambda:* but it not works
After I am creating a user:
XXUser:
Type: AWS::IAM::User
Properties:
UserName: xxx.user
LoginProfile:
Password: l98GaTc9xzT9
PasswordResetRequired: true
Path: /
And finally i am adding the user to my usergroup:
USerAdditionX:
Type: AWS::IAM::UserToGroupAddition
Properties:
GroupName: !Ref XXXGroup
Users:
- !Ref XXUser
But after login with my new user i am getting the following error when i go to lambda service:
User: arn:aws:iam::xxxxxxxxx:user/xx.user is not authorized to perform: lambda:GetAccountSettings on resource: * because no identity-based policy allows the lambda:GetAccountSettings action
Access to specific lambdas from my new user
The problem is that lambda get its permission from a role.Policies are attached to role.
You need to attach your policies to a role and then attach a role to lambda.
Please read about lambda execution role over here
You can't limit the visibility for the list of all the Lambda Functions (there is also the same "problem" on EC2 Instances and S3 Buckets permissions policy), so your user cannot interact with the Lambda because the policy that you provided have the condition on the specific resource, but he need the full read-only capability even to see the function.
You should add at least an Allow statement on lambda:ListFunctions and lambda:GetAccountSettings for Resource "*" (so on every Lambda of your account), as stated here.
You could also, as documented here, add the standard AWSLambda_ReadOnlyAccess policy to your group.

CloudFormation refusing to create AWS::KMS::Key with least privilege

Endeavouring to apply the principle of least privilege to a CMK I'm creating, the goal is to create a CloudFormation template that can be used via StackSets to the entire organisation. As a result, I want a key that can be used (kms:Encrypt, kms:Decrypt etc.) for general encryption tasks in the account, but that cannot be modified by principals in the account (specifically kms:PutKeyPolicy but not only).
I have a working policy lifted from a hand crafted key. The CloudFormation template works fine, the StackSet initiates the resource creation.
But only when I don't restrict the account principal with any conditions, which removes the least privilege principle. The CreateKey and PutKeyPolicy API calls both have an option BypassPolicyLockoutSafetyCheck for those of us idiotic enough to think they know better! Except CloudFormation doesn't expose that for AWS::KMS::Key :(
So unless I basically leave the key policy wide open, I get the following error in the Stack:
Resource handler returned message: "The new key policy will not allow you to update the key policy in the future. (Service: Kms, Status Code: 400, Request ID: xxxx, Extended Request ID: null)" (RequestToken: xxxx, HandlerErrorCode: InvalidRequest)
I've tried a variety of options for the principal, with the Condition removed (as below) the key is created, with it in no joy.
- Sid: AllowUpdatesByCloudFormation
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- kms:Describe*
- kms:List*
- kms:PutKeyPolicy
- kms:CreateAlias
- kms:UpdateAlias
- kms:UpdateKeyDescription
- kms:EnableKey
- kms:DisableKey
- kms:EnableKeyRotation
- kms:DisableKeyRotation
- kms:GetKey*
- kms:DeleteAlias
- kms:TagResource
- kms:UntagResource
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: '*'
# Condition:
# StringEquals:
# "aws:PrincipalAccount": !Sub ${AWS::AccountId}
# "kms:ViaService": !Sub "cloudformation.${AWS::Region}.amazonaws.com"
I've tried with different principal settings, including AWS: "*" and a few different Service options, and different settings on the Condition. I've tried with and without the region in the service name. I must be missing something, but I've lost a few hours scratching my head over this one.
Web searches, AWS forum searches have turned up nothing, so I'm hoping the good bhurgers of StackOverflow can guide me - and future me's searching for the same help :)
It doesn't seem possible to link to the table section on the AWS KMS API page for the condition keys on CreateKey or PutKeyPolicy but I don't think I've missed a trick with those?
Thanks in advance - Robert.
Try giving the root user all kms permissions - (kms:*)
The principle of least privilege still applies when giving root all access.
That will enable IAM User permissions.
Add additional policies to each role or user or user group.
Add a policy for key administration.
Add a policy for usage.
That is where you can fine tune your access.
Try working with this key policy and tweak it.
This is a key I use for RDS encryption in a cfn stack.
(Yes! I know that policies should be applied to user groups, not users directly for best practices... but this is inside a sandbox environment I have access to from 'aCloud guru')
KeyPolicy:
Id: key-consolepolicy-3
Version: "2012-10-17"
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: kms:*
Resource: '*'
- Sid: Allow access for Key Administrators
Effect: Allow
Principal:
AWS:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/admin"
- !Sub "arn:aws:iam::${AWS::AccountId}:user/cloud_user"
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:TagResource
- kms:UntagResource
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: '*'
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS"
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:DescribeKey
Resource: '*'
- Sid: Allow attachment of persistent resources
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS"
Action:
- kms:CreateGrant
- kms:ListGrants
- kms:RevokeGrant
Resource: '*'
Condition:
Bool:
kms:GrantIsForAWSResource: "true"

Lambda "Access Denied" when uploading to S3

I've been working with Serverless (the framework) and I've come across an issue. This might be down to my minimal knowledge of AWS and it's archicture but maybe someone can point me in the right direction.
I've created an S3 bucket with Terraform, it utilises AWS's KMS to give the bucket Server Side Encrpytion. Uploading to this bucket works fine from the CLI, but when using a Lambda created by serverless it returns and "Access Denied".
The serverless yaml has permissions for uploads to S3 and I've tested this with SSE turned off and it works fine.
What I don't understand is how to specify the key for AWS. I thought adding it to the top of the service might work (but to no avail).
Here is the yaml file:
service:
name: lambdas
awsKmsKeyArn: [KEY GOES HERE]
custom:
serverless-offline:
port: 3000
bucket:
name: evidence-bucket
serverSideEncryption: aws:kms
sseKMSKeyId: [ KEY GOES HERE]
provider:
name: aws
runtime: nodejs12.x
region: eu-west-2
iamRoleStatements:
- Effect: Allow
Action:
- s3:ListBucket
- s3:PutObject
- s3:PutObjectAcl
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:DescribeKey
Resource: "[KEY GOES HERE]"
functions:
storeEvidence:
handler: handler.storeEvidence
environment:
BUCKET: ${self:custom.bucket.name}
events:
- http:
path: store-evidence
method: post
Do I need an additional plugin? There is a lot of information about creating a bucket with serverless but not for using an existing bucket with SSE? How do I get around this "Access Denied" message?
As jarmod in the comment said, you are missing the kms:GenerateDataKey. Here I am gonna show you what exact need to add to your existing yaml shown above:
# ...
provider:
name: aws
runtime: nodejs12.x
region: eu-west-2
iamRoleStatements:
- Effect: Allow
Action:
- s3:ListBucket
- s3:PutObject
- s3:PutObjectAcl
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:DescribeKey
- kms:GenerateDataKey # <------ this is the new permission
Resource: "[KEY GOES HERE]"
#...
And it is worth to note that if your code literally just use s3:PutObject to upload, you don't need to add Encrypt,DescribeKey permissions. See: https://aws.amazon.com/premiumsupport/knowledge-center/s3-access-denied-error-kms/
If your code involves multipart upload, you do need kms:DescribeKey, kms:Encrypt and more permissions(like kms:ReEncrypt*, kms:GenerateDataKey*...) See details: https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html
Try this:
iamRoleStatements:
- Effect: Allow
Action:
- s3:*
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:*
Resource: "[KEY GOES HERE]"
If this works, you know you were missing some action. Its then a painful process of finding that missing action, or if your happy, just leave the *s in.

AWS Cloudformation Role is not authorized to perform AssumeRole on Role

I am trying to execute a cloudformation stack which contains the following resources:
Codebuild project
Codepipeline pipeline
Roles needed
While trying to execute the stack, it fails with the following error:
arn:aws:iam::ACCOUNT_ID:role/CodePipelineRole is not authorized to perform AssumeRole on role arn:aws:iam::ACCOUNT_ID:role/CodePipelineRole (Service: AWSCodePipeline; Status Code: 400; Error Code: InvalidStructureException; Request ID: 7de2b1c6-a432-47e6-8208-2c0072ebaf4b)
I created the role using a managed policy, but I have already tried with a normal policy and it does not work neither.
This is the Role Policy:
CodePipelinePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: 'This policy grants permissions to a service role to enable Codepipeline to use multiple AWS Resources on the users behalf'
Path: "/"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Resource: "*"
Effect: "Allow"
Condition: {}
Action:
- autoscaling:*
- cloudwatch:*
- cloudtrail:*
- cloudformation:*
- codebuild:*
- codecommit:*
- codedeploy:*
- codepipeline:*
- ec2:*
- ecs:*
- ecr:*
- elasticbeanstalk:*
- elasticloadbalancing:*
- iam:*
- lambda:*
- logs:*
- rds:*
- s3:*
- sns:*
- ssm:*
- sqs:*
- kms:*
This is the Role
CodePipelineRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: !Sub ${EnvironmentName}-CodePipelineRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Path: /
ManagedPolicyArns:
- !Ref CodePipelinePolicy
What intrigues me the most is that it seems like CodePipelineRole is trying to AssumeRole to itself. I'm not understanding what can be happening here.
And when I set the policy's action to *, it works! I don't know what permissions could be missing.
Thanks
It is to do with the trust relationship for the role you have created i.e. CodePipelineRole
Go to the Role in IAM
Select the Trust Relationships tab ...
Then Edit Trust Relationship to include codepipeline
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"codepipeline.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}```
It seems like, behind the scenes, AWS services keep some kind of role cache. If you try to make a role, attach a policy and create a new CodeBuild project sequentially, CodeBuild will give an unauthorized error because it can't find the role. It's similar to getting a forbidden access error on a non-existing bucket (instead of a 404). If you separate the stack in two other stacks: first you create the roles and then you create the CodeBuild, it works. I don't understand why the CLI command works instantly though.
try adding sts:AssumeRole to the list of Actions.
https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_roles.html
Cheers
I had a similar issue with EKS for some reason code build role could not assume role. I solved it by creating a user with sufficient access and by setting:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
env vars as default env vars from environment section in cloud build:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
I bet you specified RoleArn on your Source action of the CodePipeline. Try to remove it.
CodePipelinePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
...
Stages:
- Name: "Source"
Actions:
- Name: "Source"
#RoleArn: !GetAtt CodePipelineRole.Arn
The last line was the reason for the very same error in my case.

Restrict login to Enterprise Google Domain for AWS Federated Identity Pool

I'm using a federation identity pools with aws-amplify (https://aws.github.io/aws-amplify/media/authentication_guide#enabling-federated-identities) and I'd like to restrict the scope of domains to just my google domain organization (ex. johndoe#foobar.com).
There doesn't seem to be a way to lock it down on either the Google API console or the AWS Cognito Identity Pool settings, just a hint that an hd parameter can be appended to the google request to restrict it by domain (which would still require modifying the aws-amplify core package), and it still wouldn't be secure since anyone could just make the same request without the hd and gain access to cognito.
My question is this: is there a way to restrict a google oauth key to only allow #foobar.com email addresses, or to implement the same restriction with aws cognito?
I believe I found a solution (from several quick tests it seems to be working fine)
Don't try to control the hosted domain part via the Trust Relationship
in the Role.
Go to: Cognito / Edit Identity Pool / Authentication Providers
Select Google+
In "Authenticated role selection" select "Choose role with Rules"
Now require claim "hd" to be "equals" to <your-domain>
set "Role resolution" to "DENY"
Source: https://forums.aws.amazon.com/thread.jspa?messageID=527303
Here's a cloudformation stack to set everything (identity pool, roles, etc.) in one go. YOU NEED TO MAKE THE NECESSARY ADJUSTMENTS at all places marked with an EDIT HERE: comment.
AWSTemplateFormatVersion : 2010-09-09
Description : "An Identity Pool stack which uses Google for sign-in"
Resources:
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: identity_pool_a
AllowUnauthenticatedIdentities: false
SupportedLoginProviders:
# EDIT HERE:
"accounts.google.com": "11111111111-22222222222222222222222222222222.apps.googleusercontent.com"
IdentityForbiddenRole:
Type: AWS::IAM::Role
Properties:
MaxSessionDuration: 3600
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": unauthenticated
Policies:
- PolicyName: None
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action: "*"
Resource: "*"
IdentityAllowedRole:
Type: AWS::IAM::Role
Properties:
MaxSessionDuration: 3600
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": authenticated
Policies:
- PolicyName: UserPermissions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
# EDIT HERE:
Action: "s3:GetObject"
# EDIT HERE:
Resource: "arn:aws:s3:::my-bucket/*"
RoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
unauthenticated: !GetAtt IdentityForbiddenRole.Arn
authenticated: !GetAtt IdentityForbiddenRole.Arn
RoleMappings:
accounts.google.com:
AmbiguousRoleResolution: Deny
Type: Rules
RulesConfiguration:
Rules:
- Claim: hd
MatchType: Equals
# EDIT HERE:
Value: mydomain.com
RoleARN: !GetAtt IdentityAllowedRole.Arn