We're currently using G Suite as an IDP for our AWS SAML access that assumes a role within a handful account to give our G Suite users access to certain AWS resources. Each account has a similarly named role that the G Suite user can assume to give them access to certain resources in that account which is all working as expected.
I'm looking into whether I can configure that assumed role to give more fine-grained access to certain users for certain resources, without having to assign completely different roles to different users within G Suite itself.
For example, if the role assumed within the account is called "assumed_gsuite_ro" and doesn't give access to IAM, you get errors in the IAM console as such:
User: arn:aws:sts::0012345678900:assumed-role/assumed_gsuite_ro/matt#domain.com is not authorized to perform: iam:GetAccountSummary on resource: *
So I'd like to add something as such to the assumed_gsuite_ro role policy to give just my federated user access to that in IAM:
...
{
"Sid": "IAMTest",
"Effect": "Allow",
"Action": [
"iam:GetAccountSummary",
"iam:ListAccountAliases"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:sts::0012345678900:assumed-role/assumed_gsuite_ro/matt#domain.com"
}
}
}
...
However, this doesn't work as apparently the SourceArn doesn't match. Is there a value of the condition I can use that will allow me to target the specific federated user that AWS seems to know about? I was thinking something like aws:userid or aws:username might work, but I'm not sure what the values of those would be in this case.
Having delved a bit deeper and found this page in the AWS documentation which describes how the values of aws:userid is made up in a SAML/federated context, I've determined the following works:
"Condition": {
"StringLike": {
"aws:userid": "AROAROLEID:matt#domain.com"
}
}
Where AROAROLEID is the value of the "role ID" for the name of the role you're assuming (assumed_gsuite_ro in my example) which according to this page you can only get from a get-role call to the AWS CLI (it's the value of RoleId).
Related
I am working on a serverless app powered by API gateway and AWS lambda. Each lambda has a separate role for least privilege access. For tenant isolation, I am working on ABAC and IAM
Example of the role that provides get object access to s3 bucket having <TenantID> as the prefix.
Role Name: test-role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
],
// Same role ARN: Ability to assume itself
"Resource": "arn:aws:iam::<aws-account-d>:role/test-role"
}
]
}
I am assuming the same role in lambda but the the session tag as
const credentials = await sts.assumeRole({
RoleSessionName: 'hello-world',
Tags: [{
Key: 'TenantID',
Value: 'tenant-1',
}],
RoleArn: 'arn:aws:iam::<aws-account-d>:role/test-role'
}).promise();
I am trying to achieve ABAC with a single role instead of two(one role with just assuming role permission, another role with actual s3 permission) so that it would be easier to manage the roles and also won't reach the hard limit of 5000.
Is it a good practice to do so, or does this approach has security vulnerability?
It should work, but feels a bit strange to re-use the role like this. It would make more sense to me to have a role for the lambda function, and a role for the s3 access that the lambda function uses (for a total of two roles).
Also make sure that you're not relying on user input for the TenantID value in your code, because it could be abused to access another tenant's objects.
TLDR: I would not advise you to do this.
Ability to assume itself
I think there is some confusion here. The JSON document is a policy, not a role. A policy in AWS is a security statement of who has access to what under what conditions. A role is just an abstraction of a "who".
As far as I understand the question, you don't need two roles to do what you need to do. But you will likely need two policies.
There are two types of policies in AWS, of interest to this question: Identity based policies and Resource Based policies:
Identity-based policies are attached to some principal, which could be a role.
Resource-based policies are attached to a resource - which also could be a role!
A common use case of roles & policies is for permission delegation. In this case, we have:
A Role, that other principals can assume, maybe temporarily
A trust policy, which controls who can assume the role, under what conditions, and what actions they can take in assuming it. The trust policy is a special case of a resource policy, where the resource is the role itself.
A permissions policy, which is granted to anyone who assumes the role. This is a special case of an identity policy, which is granted based on the assumption of a role.
Key point: both policies are associated to the same role. There is one role, two policies.
Now, let's take a look at your policy. Clearly, it's trying to be two things at once: both a permissions policy and a trust policy for the role in question.
This part of it is trying to be the trust policy:
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
],
// Same role ARN: Ability to assume itself
"Resource": "arn:aws:iam::<aws-account-d>:role/test-role"
}
Since the "Principal" section is missing, looks like it's allowing anyone to assume this role. Which looks a bit dodgy to me, especially since one of your stated goals was "least privilege access".
This part is trying to be the permissions policy:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
This doesn't need a "Principal" section, because it's an identity policy.
Presumably you're resuing that policy as both the trust policy and the permissions policy for the given role. Seems like you want to avoid hitting the policy (not role) maximum quota limit of 5000 defined here:
Customer managed policies in an AWS account
Even if somehow it worked, it doesn't make sense and I wouldn't do it. For example, think about the trust policy. The trust policy is supposed to be a resource-based policy attached to the role. The role is the resource. So specifying a "Resource" in the policy doesn't make sense, like so:
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
Even worse is the inclusion of this in the trust policy:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
What does that even mean?
Perhaps I'm misunderstanding the question, but from what I understand my advice would be:
Keep your one role - that's OK
Create two separate policies: a trust policy & a permissions policy
Consider adding a "Principal" element to the trust policy
Attach the trust & permissions policies to the role appropriately
Explore other avenues to avoid exceeding the 5000 policy limit
It seems to be impossible to allow developers to create Lambdas and create or maintain SAM Applications in AWS without essentially having AdministratorAccess policies attached to their developer's role. AWS documents a suggested IAM setup where everyone is simply Administrator, or only has IAMFullAccess, or a even more specific set of permissions containing "iam:AttachRolePolicy" which all boils down to still having enough access to grant the AdministratorAccess permission to anyone at will with just 1 API call.
Besides creating a new AWS Account for each SAM or Lambda deployment there doesn't seem to be any secure way to manage this, but I really hope I'm missing something obvious. Perhaps someone knows of a combination of tags, permission boundaries and IAM Paths that would alleviate this?
The documentation I refer to: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-permissions.html which opens with:
There are three main options for granting a user permission to manage
serverless applications. Each option provides users with different
levels of access control.
Grant administrator permissions.
Attach necessary AWS managed policies.
Grant specific AWS Identity and Access Management (IAM) permissions.
Further down, a sample application is used to specify slightly more specific permissions:
For example, the following AWS managed policies are sufficient to
deploy the sample Hello World application:
AWSCloudFormationFullAccess
IAMFullAccess
AWSLambda_FullAccess
AmazonAPIGatewayAdministrator
AmazonS3FullAccess
AmazonEC2ContainerRegistryFullAccess
And at the end of the document an AWS IAM Policy document describes a set of permissions which is rather lengthy, but contains the mentioned "iam:AttachRolePolicy" permission with a wildcard resource for roles it may be applied on.
AWS has a PowerUserAccess managed policy which is meant for developers. It gives them access to most of the services and no access to admin activities including IAM, Organization and Account management.
You can create an IAM Group for developers (Say Developers) and add the managed policy PowerUserAccess to the group. Add developers to this group.
For deploying with SAM, the developers would need a few IAM permissions to create roles, tag roles. While rolling back a CloudFormation Stack, they may need a few delete permissions. While allowing the developers to create new roles for Lambda functions, you need to ensure they don't escalate privileges by using permissions boundary. A good starting point again would be to set the permissions boundary to PowerUserAccess. (until you figure out what is the right level of permissions)
Create a Policy something like this
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadRole",
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListRoleTags"
],
"Resource": "arn:aws:iam::ReplaceWithYourAWSAccountNumber:role/*FunctionRole*"
},
{
"Sid": "TagRole",
"Effect": "Allow",
"Action": [
"iam:UntagRole",
"iam:TagRole"
],
"Resource": "arn:aws:iam::ReplaceWithYourAWSAccountNumber:role/*FunctionRole*"
},
{
"Sid": "WriteRole",
"Effect": "Allow",
"Action": [
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:AttachRolePolicy",
"iam:PutRolePolicy",
"iam:PassRole",
"iam:DetachRolePolicy"
],
"Resource": "arn:aws:iam::ReplaceWithYourAWSAccountNumber:role/*FunctionRole*"
},
{
"Sid": "CreateRoleWithPermissionsBoundry",
"Effect": "Allow",
"Action": [
"iam:CreateRole"
],
"Resource": "arn:aws:iam::ReplaceWithYourAWSAccountNumber:role/*FunctionRole*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::aws:policy/PowerUserAccess"
}
}
}
]
}
Note: It assumes the Lambda function names in the SAM template contains the word Function in them. (Replace the AWS Account Number in the ARNs).
Now you can attach the above policy to the Developers IAM Group. (This would give the SAM deployment permissions to all the developers)
Or you can create another IAM Group for SAM developers (Say SAM-Developers) and attach the above policy to the SAM-Developers group. Now add the appropriate developers (who need to deploy using SAM) to this new IAM group (SAM-Developers).
Define the Permissions Boundary in the SAM templates as well.
Here is an example PermissionsBoundary in SAM template.
Globals:
Function:
Timeout: 15
PermissionsBoundary: arn:aws:iam::aws:policy/PowerUserAccess
With that, the developers should be able to deploy using SAM provided they do not have any restrictive permission boundary.
You can set the permission boundary to AdministratorAccess for the developers or create a new Policy which combines the permissions of PowerUserAccess and the above defined policy for 'SAM' deployments. Then set this new Policy as the permission boundary for the developers.
This solution is for reference and you can build upon this. The PowerUserAccess has been set as the permissions boundary for the Lambda function roles. The PowerUserAccess is too permissive and you should further work on this to find out the right level of permission for your developers and the Lambda functions.
Sidenote: You can use this policy to allow the users to manage their own credentials.
I have an application where I am using Cognito to authenticate users and giving temporary access to AWS Console but that user is able to see all other buckets, I want that user just should be able to see or access buckets created by him.
Currently, I have given S3FullAccess Policy to Cognito users. Can anyone suggest which policy I should attach?
As per my R&D, I can some policies are there that can restrict particular user or allow particular user but my users will be dynamic, so I cannot hard-code the values and also policies like allowing/restricting access to particular buckets, I want only users who create buckets should be able to access not other users.
This is something which i found
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::bucket-name",
"Condition": {
"StringLike": {
"s3:prefix": [
"",
"home/",
"home/${aws:userid}/*"
]
}
}
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::bucket-name/home/${aws:userid}",
"arn:aws:s3:::bucket-name/home/${aws:userid}/*"
]
}
]
}
But this is listing all buckets and the only accessible bucket is what put in the code above, I want for new user, it should show nothing and as it creates, it should show that only
This is not going to be easy and you will need to create your own policy and enforce some conventions. You have 3 options.
But first, if each user just needs their own S3 space look at S3 Prefix [here](
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-3-roles-and-policies/) Also, you can do this on the S3 resource bucket. I have a template for doing this here in gitlab
Now back to answering your question.
Option 1; They will need to set a tag when they create the bucket where an "owner" tag is equal to their identity. I striked this one out because despite being listed in the IAM policy I'm pretty sure it doesn't work with S3.
Option 2: The prefix of the bucket name is equal to their identity.
Then you can use the feature of variables and tags in IAM Policy. Read here
Note that coginto users are web federated identities so the variable aws:username is not aviable for you. Use the aws:userid variable and the value will be role id:caller-specified-role-name where role id is the unique id of the role and the caller-specified-role-name is specified by the RoleSessionName parameter passed to the AssumeRoleWithWebIdentity request
Option 3: Use IAM Access Policy
I can not find a link to the how to at the moment. But from here is a detailed description.
Q: How do I control what a federated user is allowed to do when signed in to the console?
When you request temporary security credentials for your federated
user using an AssumeRole API, you can optionally include an access
policy with the request. The federated user’s privileges are the
intersection of permissions granted by the access policy passed with
the request and the access policy attached to the IAM role that was
assumed. The access policy passed with the request cannot elevate the
privileges associated with the IAM role being assumed. When you
request temporary security credentials for your federated user using
the GetFederationToken API, you must provide an access control policy
with the request. The federated user’s privileges are the intersection
of the permissions granted by the access policy passed with the
request and the access policy attached to the IAM user that was used
to make the request. The access policy passed with the request cannot
elevate the privileges associated with the IAM user used to make the
request. These federated user permissions apply to both API access and
actions taken within the AWS Management Console.
The nice thing about this approach is you programmatically create the access policy.
I am using the AWS Javascript API and trying to get the assigned cognito id:
AWS.config.credentials.get(function(err) {
if (!err) {
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
}
});
Why does this result in a 400 error with the message below?
{"__type":"InvalidIdentityPoolConfigurationException","message":"Invalid identity pool configuration. Check assigned IAM roles for this pool."}
I have IAM roles configured for authenticated and non-authenticated users.
{
"Version": "2012-10-17",
"Statement": [{
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Effect": "Allow",
"Resource": [
"*"
]
}]
}
The most common reason for this error is your roles aren't set up to trust your identity pool. You should confirm that the identity pool id listed in your trust relationships matches the identity pool you are using.
More info on trust relationships in Amazon Cognito can be found in our developer guide.
After some digging I realized that you must add the RoleArn and AccountId to your credentials.
Even though most of the documentation out there mention this as being enough:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxx-a87e-46ed-9519-xxxxxxx',
});
This was not enough.
I had to do this:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxx-a87e-46ed-9519-xxxxx',
RoleArn: 'arn:aws:iam::xxxxx:role/Cognito_xxxxUsersUnauth_Role',
AccountId: 'xxxxxxxxx', // your AWS account ID
});
You must mention the ARN of your Role for your identity pool.
The only doc that mention it right is this one.
The wrong ones:
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-configuring.html
https://mobile.awsblog.com/post/TxBVEDL5Z8JKAC/Use-Amazon-Cognito-in-your-website-for-simple-AWS-authentication
https://blogs.aws.amazon.com/javascript/post/TxTUNTVES4AL15/Authentication-in-the-Browser-with-Amazon-Cognito-and-Public-Identity-Providers
Maybe I'm missing something but this is certainly confusing.
Check the "Trust Relationship" section of the role that is assigned to your Identity Pool, authentication users.
Make sure you have policies defining access to your Cognito pool.
The easiest way to get the requirement policy statements is,
Edit the pool
Create new role for identity pool
In IAM edit this role to copy the policy statements
Add these Trust Relationships to your required existing role
Another - probably less common - reason: Make sure that you are actually using an identity pool and if not, remove the identity pool id from your aws-exports.js.
I was getting this error after adding federated sign ins to my user pool (not identity pool). For reasons unknown my config included an aws_cognito_identity_pool_id. Removing this id solved the error for me.
I checked the Trust Relationship of my roles configured for "Authenticated role" and "Unauthenticated role" for my identity pool more than once, but still the error occured.
After reviewing my whole identity pool configuration I recognized that in
Authentication providers
Cognito
Authenticated role selection
I have chosen "Choose role from token" and my wrong configured role was the one I attached to the cognito group for my users.
So updating the Trust Relationship for this role fixed the problem.
Hope this helps someone :)
In my case, I am using SAML identity provider. The action in the IAM role policy should be: "Action": "sts:AssumeRoleWithSAML". But this is the root cause of the exception. I have to manually change it to "Action": "sts:AssumeRoleWithWebIdentity". It turns out any role created by the Cognito identity pool will use "Action": "sts:AssumeRoleWithWebIdentity". It won't check your identity provider type. I believe this is a bug.
I encountered this error and my problem turned out to be that my user was assuming an unauthenticated role because I was returning AWSTask(result:nil) from the logins() function in my custom CognitoDeveloperIdentityProvider.
I had the same error when trying to retrieve files from S3 through my Identity pool users.
Solution: You can create a role in IAM for "Web Identity". Then provide your identity pool ID and add the permissions that you want the role to have, e.g. S3FullAccess. Then navigate back to Amazon Cognito Identity pools and assign the role you just created to the unauthrole or authrole. The users in the Identity pool should now be able to access the S3 resources
Another -way less probably scenario- is that either the provider or identityPoolId you are using is invalid. I spent hours debugging a missing ENV in my code.
Had this issue, after several hours of checking our what the problem could be, found out that the Trust Policy is actually missing this line sts:TagSession in the Action List so eventually the Authenticated Trust Policy is as defined below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
**"sts:TagSession"** //this does the trick for me
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "{IDENTITY_POOL_ID}"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
I'd like to require the usage of MFA to IAM users when they log into the AWS Console. I know that's possible to do that for API access, but not sure whether is possible to achieve the same when logging into the Console.
Update
You can enforce your requirement with an IAM Policy based on an IAM condition that specifies the aws:MultiFactorAuthAge key as outlined in section IAM Policies with MFA Conditions within Configuring MFA-Protected API Access - you can enforce this at two levels:
Existence — To simply verify that the user has been authenticated with MFA, check that the aws:MultiFactorAuthAge key is not null. (If the
user has not been authenticated with MFA, this key doesn't exist and
therefore is null.)
Duration — If you want to grant access only within a specified time after MFA authentication, use a numeric condition type to compare the
key's age to a value (such as 3600 seconds).
Accordingly, a generic IAM policy for all AWS actions that simply tests for the existence of MFA authentication might look as follows:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition":
{
"Null":{"aws:MultiFactorAuthAge":"false"}
}
}
]
}
Initial Answer
This is a case of 'it just works', i.e. there's nothing to be done regarding MFA-Protected Access for the AWS Management Console specifically, insofar the console uses the API in turn and calls every API action with the logged in user's IAM credentials accordingly (once a user has configured and enabled an MFA device, the login page will require entering the MFA token automatically) - see also section Using MFA-Protected APIs Through the Console within Configuring MFA-Protected API Access:
AWS evaluates MFA-protected API policies for actions in the console, such as terminating an Amazon EC2 instance. Set up the IAM user with an MFA device and enable an MFA-protected API policy. The user can then simply log into the console with MFA authentication and is subject to the policies for MFA-protected APIs. For users who already have an assigned MFA device, the console experience doesn't change (except for optional time limits on certain MFA-protected APIs that require more frequent re-authentication). For more information on setting up an IAM user with an MFA device, see Setting Up an MFA Device.
Requiring MFA for the Web Console
The IAM policy listed here will do the following:
Allow users to change their own password
Allow users to view and edit their own MFA devices (a necessary component to requiring them to have MFA)
Prevent users from doing most things if they logged in to the console without MFA
Still allow users to set up MFA if they logged in without it
Use API keys regardless of MFA
It is best to create this as an IAM policy, and then attach it to the user groups that have console users. You can also attach the policy to a user directly, but that's harder to manage.
Note that adding an MFA device to your account is insufficient to be able to access AWS resources; you need to have logged in with that MFA device for it to work. Thus, to get set up with MFA, you need to do the following steps:
Sign in to the AWS console.
In the top right, click the dropdown labeled with your username
Go to "Security Credentials"
Add an MFA device
Log out
Log back in using MFA; you can now use the AWS console normally
The IAM policy is the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:ChangePassword",
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ResyncMFADevice",
"iam:DeleteVirtualMFADevice",
"iam:DeactivateMFADevice",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ListAccessKeys",
"iam:GetAccessKeyLastUsed",
"iam:GetUser"
],
"Resource": [
"arn:aws:iam::*:mfa/${aws:username}",
"arn:aws:iam::*:user/${aws:username}"
]
},
{
"Sid": "BlockMostAccessUnlessSignedInWithMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:ListVirtualMFADevices",
"iam:EnableMFADevice",
"iam:ResyncMFADevice",
"iam:DeleteVirtualMFADevice",
"iam:DeactivateMFADevice",
"iam:ListAccountAliases",
"iam:ListUsers",
"iam:ListSSHPublicKeys",
"iam:ListAccessKeys",
"iam:GetAccessKeyLastUsed",
"iam:ListServiceSpecificCredentials",
"iam:ListMFADevices",
"iam:GetAccountSummary",
"iam:ChangePassword",
"iam:GetUser",
"sts:GetSessionToken"
],
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
Note that while this allows use of most of the AWS API without needing MFA, some API calls won't work. In particular, any calls that use temporary AWS authentication credentials will fail. For example:
Using ECR login, as this generates a temporary key that it gives to docker
Retrieving a value from AWS Secrets Manager, as this uses temporary credentials to talk to KMS
Elastic Beanstalk operations, as these seem to use temporary credentials internally
In order to do these things, you need to get MFA API credentials (see below). The other option (especially good for ECR login) is to create a less privileged user in a group that doesn't have the MFA Required policy to use for making the API calls in question.
Requiring MFA Everywhere
For enhanced security, you can require MFA access for both the web console and the API. To do this, change the condition in the above policy to be the following:
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
The reason that this works is that API calls with permanent credentials don't pass the MultiFactorAuthPresent key at all, whereas console requests always do. Changing this condition from Bool to BoolIfExists changes the condition to resolve to true if the MultiFactorAuthPresent key does not exist, which in turn blocks the requests.
To use the API with MFA, you need to use the sts get-session-token command. For example, with the AWS CLI:
# Get the serial number of your MFA device
aws --profile <PROFILE_NAME> iam list-mfa-devices --user-name <IAM_USER_NAME>
# Get temporary API keys that will pass MFA verification
aws --profile <PROFILE_NAME> sts get-session-token --serial-number <MFA_SERIAL_NUMBER> --token-code <MFA_TOKEN>
# Export the temporary credentials for use in subsequent calls
export AWS_ACCESS_KEY_ID=<KEY>
export AWS_SECRET_ACCESS_KEY=<SECRET>
export AWS_SESSION_TOKEN=<SESSION_TOKEN>
If you prefer a script that manages the credential acquisition process, use the command-line tool iam-mfa: https://github.com/zagaran/iam-mfa. (Disclaimer: I am the primary author of this tool.)
In the meantime AWS itself has provided a tutorial on how to force Users to use a MFA device while still enabling them to manage a MFA device on their own. This is quite similar to the updated answer from Steffen, but differs in the details.
It works by
Creating a IAM Policy based on this official AWS template which basically forbids everything except IAM operations without an active MFA Login
Assign the Policy to you relevant IAM users – or better – IAM Groups.
After you created IAM users and passed them their initial credentials, the users will receive a You are not authorized to perform this operation. on all operations except on accessing the IAM security console.
After registering a MFA device, logging out and then again in with their new MFA token they will be able to operate everything as expected.