How do I set the account ID or profile / role for CognitoIdentityProviderClient's addCustomAttributes? - amazon-cognito

I'm interfacing with CognitoIdentityProviderClient as such:
class CognitoService
{
protected $client;
public function __construct(CognitoIdentityProviderClient $client)
{
$this->client = $client;
}
public function addCustomAttributes(string $userPoolName, string $attributes)
{
$userPool = $this->getResource($userPoolName);
return $this->client->addCustomAttributes([
'UserPoolId' => $userPool['PhysicalResourceId'],
'CustomAttributes' => $attributes,
]);
}
How do I set the account ID or profile / role for addCustomAttributes?

The fix was to ensure the IAM role for that particular action was able to do that to that resource and user pool
e.g.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"cognito-idp:AdminGetUser",
"cognito-idp:AdminUpdateUserAttributes",
"cognito-idp:ChangePassword",
"cognito-idp:ConfirmForgotPassword",
"cognito-idp:DeleteUser",
"cognito-idp:GetUser",
"cognito-idp:ListUsers",
"cognito-idp:SignUp",
"cognito-idp:UpdateUserAttributes"
],
"Resource": [
"arn:aws:cognito-idp:eu-west-1:123456789:userpool/eu-west-1_abcdef",
"arn:aws:cognito-idp:eu-west-1:123456789:userpool/eu-west-1_ghijk",
"arn:aws:cognito-idp:eu-west-1:123456789:userpool/eu-west-1_lmnop"
],
"Effect": "Allow"
}
]
}

Related

AWS Cognito IAM policy - How to limit access to S3 folder (.NET SDK ListObjectsV2)

I am trying to limit access for a Cognito user to specific folders in a bucket. The final target is to reach what is described here but I've simplified it for debugging.
The structure is as follows
MYBUCKET/111/content_111_1.txt
MYBUCKET/111/content_111_2.txt
MYBUCKET/222/content_222_1.txt
MYBUCKET/333/
I am performing a simple "list" call via the SDK.
using (AmazonS3Client s3Client = new AmazonS3Client(cognitoAWSCredentials))
{
ListObjectsV2Request listRequest = new()
{
BucketName = "MYBUCKET"
};
ListObjectsV2Response listResponse = await s3Client.ListObjectsV2Async(listRequest);
}
I am authenticating via Cognito so I am updating Cognito's IAM policy linked to the authenticated role.
The following policy returns an S3 exception "Access Denied":
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MYBUCKET",
"Condition": {
"StringLike": {
"s3:prefix": [
"111",
"111/*"
]
}
}
}
]
}
The following policy returns all results (as expected).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MYBUCKET"
}
]
}
This is supposed to be super straightforward (see here ). There are other similar questions (such as this and others) but with no final answer.
How should I write the IAM policy so that authenticated users can only access the contents of the folder "111"?
Best regards,
Andrej
I hope I now understand what I got wrong."s3:prefix" is not some form of "filter that will only return the objects that match the prefix"; it is "a parameter that forces the caller to provide specific prefix information when executing the operation".
The following is taken from the S3 documentation :
To answer my own question, starting from the IAM policy above
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::MYBUCKET",
"Condition": {
"StringLike": {
"s3:prefix": [
"111",
"111/*"
]
}
}
}
]
}
If I call the SDK with the code below, I will indeed get an "Access Denied" because I have not specified a prefix that matches the IAM policy.
using (AmazonS3Client s3Client = new AmazonS3Client(cognitoAWSCredentials))
{
ListObjectsV2Request listRequest = new()
{
BucketName = "MYBUCKET"
};
ListObjectsV2Response listResponse = await s3Client.ListObjectsV2Async(listRequest);
}
But if I do specify the prefix in my SDK call, S3 will return the expected results i.e., only the ones "starting with 111".
using (AmazonS3Client s3Client = new AmazonS3Client(cognitoAWSCredentials))
{
ListObjectsV2Request listRequest = new()
{
BucketName = "MYBUCKET",
Prefix = "111"
};
ListObjectsV2Response listResponse = await s3Client.ListObjectsV2Async(listRequest);
}
In other words, my problem was not in the way I had written the IAM policy but in the way I was expecting the "s3:prefix" to work.

How to write VPC flow logs to an S3 bucket on another AWS account?

I am trying to write VPC Flow logs (from account 1) to an S3 bucket (on account 2), using terraform:
resource "aws_flow_log" "security_logs" {
log_destination = "arn:aws:s3:::my_vpcflowlogs_bucket"
log_destination_type = "s3"
vpc_id = var.vpc_id
traffic_type = "ALL"
}
resource "aws_iam_role" "vpc_flow_logs" {
name = "vpc_flow_logs"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "write_vpc_flow_logs" {
name = "write_vpc_flow_logs"
role = aws_iam_role.vpc_flow_logs.id
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogDelivery",
"logs:DeleteLogDelivery"
],
"Resource": "arn:aws:s3:::my_vpcflowlogs_bucket"
}
]
})
}
Account 1 & 2 belong to the same organisation.
I am getting the following response:
Error creating Flow Log for (vpc-xxxxxxxxxxxx), error: Access Denied for LogDestination: my_vpcflowlogs_bucket. Please check LogDestination permission
How can I make this work? This bucket contains sensitive information, therefore i have restricted every kind of public access.
I am guessing that there is a way to allow certain principals to write into the bucket even from different accounts, but I am unaware how.
S3 Policy
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid": "AWSLogDeliveryWrite",
"Effect": "Allow",
"Principal": {"Service": "delivery.logs.amazonaws.com"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my_vpcflowlogs_bucket/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"aws:SourceAccount": <account-1-id>
},
"ArnLike": {
"aws:SourceArn": "arn:aws:logs::<account-1-id>:*"
}
}
},
{
"Sid": "AWSLogDeliveryCheck",
"Effect": "Allow",
"Principal": {"Service": "delivery.logs.amazonaws.com"},
"Action": ["s3:GetBucketAcl", "s3:ListBucket"],
"Resource": "arn:aws:s3:::my_vpcflowlogs_bucket",
"Condition": {
"StringEquals": {
"aws:SourceAccount": <account-1-id>
},
"ArnLike": {
"aws:SourceArn": "arn:aws:logs::<account-1-id>:*"
}
}
}
]
}

Is it possible to create a trust that allow federated users from cognito to assume role according to a custom attribute?

Is it possible to create a trust that allow federated users from cognito to assume role according to a custom attribute?
For example with a tenant attribute:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:12345678-corner-cafe-123456790ab"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "unauthenticated"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:custom:tenant": "tenant-name"
}
}
}
]
}
I'm using get_credentials_for_identity api with cognito token to assume this role.
Yes, this is definitely possible with attributes for access control. This will only work for an authenticated user (you need an ID token to map claims to principal tags).
Your trust policy needs to looks like this:
{
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession"
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:12345678-corner-cafe-123456790ab"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
},
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
}
}
]
}
The difference is the sts:TagSession action and the fact that amr must be authenticated.
Policy for assumed role will look something like this:
{
"PolicyDocument": {
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Resource": "arn:aws:s3:::your-bucket-name/cognito/attributes/by_family_name/${aws:PrincipalTag/family_name}/*"
},
{
"Action": "s3:GetObject",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/given_name": "${aws:PrincipalTag/given_name}"
}
},
"Effect": "Allow",
"Resource": "arn:aws:s3:::your-bucket-name/cognito/attributes/by_tag/*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "attributes-policy"
}
You need to configure your Cognito (or any other) provider in Identity Pool to do the mapping between claims from the token to tags. You can do it in console under Attributes for access control. Or with CLI like this:
aws cognito-identity set-principal-tag-attribute-map --cli-input-json file://set-principal-tag-attribute-map.json
Where set-principal-tag-attribute-map.json would look something like this (for Cognito as IDP):
{
"IdentityPoolId": "here-is-your-identity-pool-id",
"IdentityProviderName": "cognito-idp.<region>.amazonaws.com/<user_pool_id>",
"UseDefaults": false,
"PrincipalTags": {
"given_name": "given_name",
"family_name": "family_name"
}
}
You can find more details in documentation here: https://docs.aws.amazon.com/cognito/latest/developerguide/attributes-for-access-control.html

How to grant lambda permission to upload file to s3 bucket in `terraform`?

I have below lambda function configuration in TerraForm:
resource "aws_lambda_function" "test_lambda" {
# filename = "crawler/dist/deploy.zip"
s3_bucket = "${var.s3-bucket}"
s3_key = "${aws_s3_bucket_object.file_upload.key}"
# source_code_hash = "${filebase64sha256("file.zip")}"
function_name = "quote-crawler"
role = "arn:aws:iam::773592622512:role/LambdaRole"
handler = "handler.handler"
source_code_hash = "${data.archive_file.zipit.output_base64sha256}"
runtime = "${var.runtime}"
timeout = 180
environment {
variables = {
foo = "bar"
}
}
}
when I run the lambda I got the error "errorMessage": "An error occurred (AccessDenied) when calling the PutObject operation: Access Denied", when it tries to upload file to s3 bucket. It seems that the lambda function doesn't have permission to access s3. TerraForm doc is not clear about how to configure them. The permission configuration panel doesn't appear on lambda console either. It seems that lambda that created by TerraForm has limited configuration for me to use. So how can I grant s3 permission to lambda?
To make it easy you can do this in three steps,
create a role
create policy
attached policy to the role
attached role to lambda
Create role.
resource "aws_iam_role" "role" {
name = "${var.env_prefix_name}-alb-logs-to-elk"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
Create a policy that has specified access to s3
#Created Policy for IAM Role
resource "aws_iam_policy" "policy" {
name = "${var.env_prefix_name}-test-policy"
description = "A test policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": "arn:aws:s3:::*"
}
]
}
EOF
}
Attached IAM Role and the new created Policy
resource "aws_iam_role_policy_attachment" "test-attach" {
role = "${aws_iam_role.role.name}"
policy_arn = "${aws_iam_policy.policy.arn}"
}
Now attached the role to Lamba source
resource "aws_lambda_function" "test_lambda" {
# filename = "crawler/dist/deploy.zip"
s3_bucket = "${var.s3-bucket}"
s3_key = "${aws_s3_bucket_object.file_upload.key}"
# source_code_hash = "${filebase64sha256("file.zip")}"
function_name = "quote-crawler"
role = "${aws_iam_role.role.arn}"
handler = "handler.handler"
source_code_hash = "${data.archive_file.zipit.output_base64sha256}"
runtime = "${var.runtime}"
timeout = 180
environment {
variables = {
foo = "bar"
}
}
}
The IAM role associated to the function is not allowed to upload to S3.
The solution is to create an IAM policy allowing S3 access to your bucket (say read/write), which would look something like:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListObjectsInBucket",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket-name"]
},
{
"Sid": "AllObjectActions",
"Effect": "Allow",
"Action": "s3:*Object",
"Resource": ["arn:aws:s3:::bucket-name/*"]
}
]
}
Then, you need to attach this policy to the role used by your lambda function.
More info at:
https://www.terraform.io/docs/providers/aws/r/iam_role_policy.html
I would do it in the following order:
this code is using terraform 0.12.*
Create policy documents for assume role and s3 permissions
data aws_iam_policy_document lambda_assume_role {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
data aws_iam_policy_document lambda_s3 {
statement {
actions = [
"s3:PutObject",
"s3:PutObjectAcl"
]
resources = [
"arn:aws:s3:::bucket/*"
]
}
}
Create an IAM policy
resource aws_iam_policy lambda_s3 {
name = "lambda-s3-permissions"
description = "Contains S3 put permission for lambda"
policy = data.aws_iam_policy_document.lambda_s3.json
}
Create a role
resource aws_iam_role lambda_role {
name = "lambda-role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
Attach policy to role
resource aws_iam_role_policy_attachment lambda_s3 {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_s3.arn
}
Attach role to lambda
resource "aws_lambda_function" "test_lambda" {
# filename = "crawler/dist/deploy.zip"
s3_bucket = var.s3-bucket
s3_key = aws_s3_bucket_object.file_upload.key
# source_code_hash = "${filebase64sha256("file.zip")}"
function_name = "quote-crawler"
role = aws_iam_role.lambda_role.arn
handler = "handler.handler"
source_code_hash = data.archive_file.zipit.output_base64sha256
runtime = var.runtime
timeout = 180
environment {
variables = {
foo = "bar"
}
}
}

How to access AWS S3 bucket logging in with Google and Cognito User/Identity Pool

I'm using the AWS Cognito Enhanced (Simplified) Flow to get the Cognito Identity Credentials providing the idToken received after logging in with Google sign-in api:
export function getAWSCredentialWithGoogle(authResult) {
if (authResult['idToken'] != null) {
AWS.config.region = 'eu-central-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'eu-central-1:xxxxxxxxxxxxxxxxxxxxxxxx',
Logins: {
'accounts.google.com': authResult['idToken']
}
})
return AWS.config.credentials.getPromise()
.then(
function(){
return getAWSCredentials(AWS.config.credentials);
},
function(err) {
}
)
} else {
console.log('no auth code found!');
}
}
I get the:
accessKeyId:"ASIAXXXXXX",
secretAccessKey:"ta4eqkCcxxxxxxxxxxxxxxxxxxx",
sessionToken:"xxxxxxxxx...etc..."
Then I try to upload a picture to an S3 bucket passing the above received accessKeyId and secretAccessKey.
But I receive this error result:
InvalidAccessKeyIdThe AWS Access Key Id you provided does not exist in our records.ASIAXXXXXXXXXXXXXXXX
This is how I set up the AWS S3 (managed) policy (the resource policy for the bucket is the default one) to access progammaticaly the bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::xxxxxxxxxx",
"arn:aws:s3:::xxxxxxxxxx/users"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"${cognito-identity.amazonaws.com:sub}/*"
]
}
}
},
{
"Action": [
"s3:PutObject",
"s3:GetObjectAcl",
"s3:GetObject",
"s3:PutObjectVersionAcl",
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:GetObjectVersion"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::xxxxxxxxx/users/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::xxxxxxxxx/users/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
this policy has been attached to an IAM role with the following trust relationship:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "eu-central-1xxxxxxxxxxx"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
I've properly configured the Federated Identity Pool to use this role, and added Google as an OpenID Connect provider.
I also configured my Cognito Identity Pool to accept users federated with my Cognito User Pool by supplying the User Pool ID and the App Client ID.
I would like to to give to any Google sign-in authenticated user, access to the S3 bucket and have read/write permission to his own directory.