AWS cognito identity pool ABAC how to map custom multi-valued attributes? - amazon-web-services

Example open id token from the identity provider (Cognito user pool in this example):
{
"cognito:groups": [
"testers",
"admins",
],
"email_verified": false,
...
}
I want to use ABAC, like the example given here: https://docs.aws.amazon.com/cognito/latest/developerguide/using-attributes-for-access-control-policy-example.html
So that I can add policy statements to the role associated with the Cognito identity pool with conditions, example:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "rds-db:connect",
"Resource": "arn:aws:rds-db:eu-west-1:123456789012:dbuser:cluster-teapot/db-user",
"Effect": "Allow",
"Condition": {
"ForAnyValue:StringLike": {
"aws:PrincipalTag/groups": "admins"
}
}
}
]
}
It works like a charm if you use single valued custom claims, like the "email_verified" above, but if I try to map array-valued claims like "cognito:groups"
aws cognito-identity get-credentials-for-identity...
fails with: Invalid identity pool configuration. Check assigned IAM roles for this pool.
This is not a problem with trust since its tried and tested with single-valued claims, so I am wondering if someone knows if the syntax is wrong, or if this is a missing feature?

Related

AWS SNS ConfirmSubscription authorization never granted to IAM user

Goal: Grant permissions to IAM user to confirm an SNS Topic Subscription
Problem: Using AWS Web Console, I am unable to grant the proper SNS permissions to the IAM user account no matter what permissive policies I attach to it.
Steps completed: I created a Topic which tracks changes to S3 objects and pushes that information to my application via an SNS subscription (HTTPS/JSON calls).
My code that receives the request:
def self.confirm(arn, token)
client = retrieve_client
client.confirm_subscription(topic_arn: arn, token: token)
end
def self.retrieve_client
creds = Aws::Credentials.new(
Rails.application.credentials.dig(:aws, :access_key_id),
Rails.application.credentials.dig(:aws, :secret_access_key)
)
Aws::SNS::Client.new(region: 'us-east-2', credentials: creds)
end
When my code receives the SNS confirmation request, I receive this error message:
Aws::SNS::Errors::AuthorizationError (User: arn:aws:iam::12345678912:user/user_name is not authorized to perform: SNS:ConfirmSubscription on resource: arn:aws:sns:us-east-2:12345678912:topic_name because no boundary policy allows the SNS:ConfirmSubscription action)
The above code works well with a different application (but different IAM user), so I don't believe the code is the culprit, yet.
I've attempted adding policies to a group, then the user to the group, nothing changes.
I've resorted to directly adding policies to the user, nothing changes.
Here are the two most permissive policies I've tried and I don't know what other blanket permissions I can give this user to make this subscription confirmation work.
Topic arn: arn:aws:sns:us-east-2:12345678912:topic_name
Topic access policy:
{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "sns:Publish",
"Resource": "arn:aws:sns:us-east-2:12345678912:topic_name
"StringEquals": {
"AWS:SourceAccount": "12345678912"
},
"ArnLike": {
"AWS:SourceArn": "arn:aws:s3:*:*:*"
}
}
}
]
}
Policy 1 (from the AWS Managed AmazonSNSFullAccess policy):
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sns:*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Policy 2, where I just click as many Action selections as possible to see if anything will work:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"sns:TagResource",
"sns:DeleteTopic",
"sns:ListTopics",
"sns:Unsubscribe",
"sns:CreatePlatformEndpoint",
"sns:SetTopicAttributes",
"sns:UntagResource",
"sns:OptInPhoneNumber",
"sns:CheckIfPhoneNumberIsOptedOut",
"sns:ListEndpointsByPlatformApplication",
"sns:SetEndpointAttributes",
"sns:Publish",
"sns:DeletePlatformApplication",
"sns:SetPlatformApplicationAttributes",
"sns:VerifySMSSandboxPhoneNumber",
"sns:Subscribe",
"sns:ConfirmSubscription",
"sns:RemovePermission",
"sns:ListTagsForResource",
"sns:DeleteSMSSandboxPhoneNumber",
"sns:ListSubscriptionsByTopic",
"sns:GetTopicAttributes",
"sns:ListSMSSandboxPhoneNumbers",
"sns:CreatePlatformApplication",
"sns:SetSMSAttributes",
"sns:CreateTopic",
"sns:GetPlatformApplicationAttributes",
"sns:GetSubscriptionAttributes",
"sns:ListSubscriptions",
"sns:AddPermission",
"sns:ListOriginationNumbers",
"sns:DeleteEndpoint",
"sns:ListPhoneNumbersOptedOut",
"sns:GetEndpointAttributes",
"sns:SetSubscriptionAttributes",
"sns:GetSMSSandboxAccountStatus",
"sns:CreateSMSSandboxPhoneNumber",
"sns:ListPlatformApplications",
"sns:GetSMSAttributes"
],
"Resource": "*"
}
]
}
It's completely unclear to me what other policies are required to give this user the authorization necessary to confirm the SNS subscription.
Thanks to the question Marcin asked about the "boundary policy," I learned that the concept of an IAM boundary policy existed, what it was, and then fixed my problem.
At some point, when the IAM user was setup, a boundary policy was attached to the user account, which precludes any other policies that may be given to that user by other service or group policies.
Thus, when I inspected the IAM user in question, I found a boundary policy that only permitted access to AWS S3 services. This policy prevented my efforts to give the user access to AWS SNS services.
After removing the boundary policy, the IAM user settings now read "Permissions boundary (not set)" and the confirmation of the SNS subscriptions work as expected.
Thanks for the help, Marcin!

AWS IAM Policy grant permissions for some EC2 instances

I want to restrict access for a specific user to see just few EC2 instances. I created a new user in IAM Roles and I attached a new Policy to it. The content of that Policy is attached below. I tried to look over documentation and to do it myself like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": [
"arn:aws:ec2:eu-west-1:ACCOUNT_ID:instance/i-INSTANCE_ID1",
"arn:aws:ec2:eu-west-1:ACCOUNT_ID:instance/i-INSTANCE_ID2"
]
}
]
}
I placed my region,ACCOUNT_ID(the owner id, not of the new user created) and instance-id, but when I connect with that user and I go to list all Instances I got this An error occurred fetching instance data: You are not authorized to perform this operation..
After I placed the code in JSON editor, in Policy Review step I got this message:
This policy defines some actions, resources, or conditions that do not
provide permissions. To grant access, policies must have an action
that has an applicable resource or condition. For details, choose Show
remaining Learn more
The AWS documentation mention exactly the same configuration or these examples.
I assume you connect as that user in the console (but it would be the same with CLI) Here is what I think is happening:
To list all the instances, the console most probably calls the DescribeInstances API. As per the list of action/resources/tags that can be used in IAM policy, this API does not support the resource filter in IAM.
This means your user has no authorization to list instances and they will not be shown in the console. You can validate this theory by using the CLI to request the details of a specific instance id, if my hypothesis is correct, it will be authorized.
As DescribeInstances can not be restricted by resource or tags, I don't think it is possible to filter the instance list for a user.
To have the console working, you'll need to add the following statement in your IAM policy
"Statement": [
{ your existing statement },
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
}
]
Please report if I was right :-) The example you mentioned in your question shows exactly that : Resources = * on DescribeInstances and Resources specific InstanceId on other operations.
The previous answer is wrong, you can Conditionally allow access to ec2:DescribeInstances by tag names. It's an AWS best practice as well. Also explicitly deny access to the ec2:CreateTags and ec2:DeleteTags actions to prevent users from creating or deleting tags to take control of the instance.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/UserName": "${aws:username}"
}
}
},
{
"Effect": "Deny",
"Action": [
"ec2:CreateTags",
"ec2:DeleteTags"
],
"Resource": "*"
}
]
}
DescribeInstances action does not support condition.
https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html

Public queries and mutations (no authentication)

The documentation says there are 3 ways we can authorise an application to interact with the API, but it doesn't look like there is a way of having a public endpoint.
For example, if I want anyone to query a list of todos, but only authenticated users can add a todo to that list, how can I achieve this?
Or if I want to allow anyone to do a schema introspection, but restrict all other queries to authenticated users, is it possible?
I'm using cognito for authentication. I noticed there is a AppId client regex field that says (Optional) Type a regular expression to allow or block requests to this API. but I can't find any example unfortunately. Maybe this is what I'm looking for?
Thanks
Julien
There are couple of ways in which you can do this based on Authentication mechanism.
Say you are using Cognito Identity and using AWS IAM flow for authentication. Then you would have 2 policies one for Authenticated User and One for Unauthenticated User.
Given a GraphQL Schema
schema{
query:Query
mutation:Mutation
}
type Query{
listTodo(count:Int, paginationToken:String):[TodoConnection];
}
type Mutation{
addTodo(input:TodoInput):Todo
}
Your Unauthenticated policy would look something like
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"appsync:GraphQL"
],
"Resource": [
"arn:aws:appsync:us-west-2:<account-id>:apis/<api-id>/types/Query/fields/listTodo",
//-> below is for schema introspection
"arn:aws:appsync:us-west-2:<account-id>:apis/<api-id>/types/Query/fields/__schema"
]
]
}
}
Your authenticated user policy would look like
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"appsync:GraphQL"
],
"Resource": [
"arn:aws:appsync:us-west-2:<account-id>:apis/<api-id>/types/Mutation/fields/addTodo",
"arn:aws:appsync:us-west-2:<account-id>:apis/<api-id>/types/Query/fields/listTodo",
//-> below is for schema introspection
"arn:aws:appsync:us-west-2:<account-id>:apis/<api-id>/types/Query/fields/__schema"
]
]
}
}
If you are using JWT Tokens then you will have to associate each Cognito User Pool User with a Group (like "Admin", "Users" etc). You then will have to associate each of the query/mutation with the Cognito Groups that can perform the operation using AWS AppSync auth directives. To do you you will only need to update the schema like below:
schema{
query:Query
mutation:Mutation
}
type Query{
listTodo(count:Int, paginationToken:String):[TodoConnection];
#aws_auth(cognito_groups:["Users", "Admin"])
}
type Mutation{
addTodo(input:TodoInput):Todo
#aws_auth(cognito_groups:["Admin"])
}
API Key based authentication, its not possible to have control over the operation.

Prompt for MFA code on certain AWS Cli actions

We force MFA for AWS web console access. But I also want some aws actions to be prompted for MFA code.
aws iam delete-users --user-name theusername
Enter MFA: *********
Is this possible?
You should be able to add an MFA condition on the relevant API actions. For example, here's a IAM policy that allows the bearer to invoke EC2 actions freely, but requires MFA when invoking StopInstances or TerminateInstances.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ec2:*"],
"Resource": ["*"]
},{
"Effect": "Deny",
"Action": ["ec2:StopInstances", "ec2:TerminateInstances"],
"Resource": ["*"],
"Condition": {"BoolIfExists": {"aws:MultiFactorAuthPresent": false}}
}]
}
For a more detailed example, see here.
The exact use case is not possible. However, you can give the necessary permission e.g. StopInstances to an IAM Role and give the IAM user permission only to assume the role if and only if the user uses MFA. The role's trust policy will be as following:
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole",
"Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }
}
}
So while using the Console, IAM user will sign in with credentials and MFA token and will be able to assume the role to stop the instance.
While using CLI, you can use named profiles with "mfa_serial" variable and when the user will try to stop the instance with the named profile parameter, the CLI will ask for the MFA code (note that the returned credentials will be cached in CLI).
Alternatively, you can use the suggestion provided by jarmod and use custom script because you need to call GetSessionToken and pass the MFA token. There is a sample Python and C# script here.

IAM policy for API Gateway invocation based on Cognito Identity ID

I want to allow Cognito authenticated users to invoke API Gateway endpoint but restrict them to their own resources like
'/users/<IdentityID>/*'.
I have prepared an IAM role like this.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:ap-northeast-1:*:MyAPIID/*/*/users/${cognito-identity.amazonaws.com:sub}*"
]
}
}
But on this setting, I get a 403 error when I try to invoke.
If I replace the ${cognito-identity.amazonaws.com:sub} to actual Identity ID (like ap-northeast-1%3Ad8515ae9-62b5-4cba-af5c-195f5d7e1d07), it works.
We cannot use ${cognito-identity.amazonaws.com:sub} on API Gateway resource, can we?
That is correct. Currently, it's only a shortcut for S3 and DynamoDB.