AWS: Enforce MFA for Console users but not CLI - amazon-web-services

I am trying to give admin rights to a specific user group and enforcing MFA for the same group. MFA should be only enforced for console users though, not when using the AWS CLI.
These are the policies I have been testing with:
Policy 1 - Administrator access granted if MFA
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAdminAccessIfSignedInWithMFA",
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
Policy 2 - Still, allow the user to set up MFA in case it is not yet active
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowViewAccountInfo",
"Effect": "Allow",
"Action": "iam:ListVirtualMFADevices",
"Resource": "*"
},
{
"Sid": "AllowManageOwnVirtualMFADevice",
"Effect": "Allow",
"Action": [
"iam:CreateVirtualMFADevice",
"iam:DeleteVirtualMFADevice"
],
"Resource": "arn:aws:iam::*:mfa/${aws:username}"
},
{
"Sid": "AllowManageOwnUserMFA",
"Effect": "Allow",
"Action": [
"iam:DeactivateMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ResyncMFADevice"
],
"Resource": "arn:aws:iam::*:user/${aws:username}"
}
]
}
This last policy was adapted from this one.
The official documentation for aws:MultiFactorAuthPresent says, as I understand it, that the combination of "Allow", "BoolIfExists" and "true" should work well for my purpose:
This condition matches either if the key exists and is present or if the key does not exist. This combination of Allow, BoolIfExists, and true allows requests that are authenticated using MFA, or requests that cannot be authenticated using MFA. This means that AWS CLI, AWS API, and AWS SDK operations are allowed when the requester uses their long-term access keys. This combination does not allow requests from temporary credentials that could, but do not include MFA.
If you are wondering why I'm not using any (seemingly simpler) policy containing a "Deny" effect, like:
"Effect" : "Deny",
"Condition" : { "BoolIfExists" : { "aws:MultiFactorAuthPresent" : "false" } }
...the reason is:
This combination of Deny, BoolIfExists, and false denies requests that are not authenticated using MFA. Specifically, it denies requests from temporary credentials that do not include MFA. It also denies requests that are made using long-term credentials, such as AWS CLI or AWS API operations made using access keys. The *IfExists operator checks for the presence of the aws:MultiFactorAuthPresent key and whether or not it could be present, as indicated by its existence. Use this when you want to deny any request that is not authenticated using MFA. This is more secure, but can break any code or scripts that use access keys to access the AWS CLI or AWS API.
Everything works as expected, except for when I try to access resources using the AWS CLI (with an access key).
Am I missing something or is the documentation misleading?
PS: I would like to avoid having separate user groups for console and CLI users.

Related

Disallow a user to list directory contents of S3 bucket

I am attempting to allow multiple users access to a single S3 bucket. However they should only have access to a particular directory in that bucket.
Imagine the following
my-bucket
- client-1
- important-doc.txt
- client-2
- somefile.jpg
- my-own-file.js
With that in mind (allowing say, client-1 access to only that directory) I have the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::my-bucket/client-1/*"
}
]
}
This works as you would expect, client-1 can connect to the bucket, go to their particular directory and download. However it appears they have the ability to list the directory of the entire bucket, I assume due to the s3:ListBucket permission being allowed. But if I restrict that to only the folder my Transmit app notifies me that permission is denied.
Can anyone advise me how to correctly write this permission?
The first choice is how to track and authenticate the users.
Option 1: IAM Users
Normally, IAM User credentials are given to employees and applications. Policies can be applied directly against IAM Users to grant them access to resources.
In the case of granting IAM Users access to specific folders within an Amazon S3 bucket, the easiest method would be to put these users into an IAM Group and then author a policy that uses IAM Policy Variables that can automatically insert the name of the IAM User into the policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["s3:ListBucket"],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::mybucket"],
"Condition": {"StringLike": {"s3:prefix": ["${aws:username}/*"]}}
},
{
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::mybucket/${aws:username}/*"]
}
]
}
There is a limit of 5000 IAM Users in an AWS Account.
Option 2: Temporary credentials
Rather than giving IAM User 'permanent' credentials, the AWS Security Token Service (STS) can issue temporary credentials with an assigned permission policy.
The flow would typically be:
Users authenticate themselves to your app (eg using your own database of users, or federated access from Active Directory, or even an OpenID service)
Your back-end app then generates temporary credentials with the appropriate permissions (such as the policy you have shown in #jellcsc's answer)
The app provides these credentials to the users (or their app)
The users use these credentials to access the permitted AWS services
The credentials expire after a period of time and the users must reconnect to your app to obtain a new set of temporary credentials.
This is more secure because the app is responsible for ensuring authentication and granting permissions. There is less risk of accidentally granting permissions to a set of users.
Option 3: Pre-signed URLs
When a web application wishes to allow access to private objects in Amazon S3, it can generate an Amazon S3 pre-signed URLs, which is a time-limited URL that provides temporary access to a private object. It works like this:
Users authenticate to the web app
When the back-end is rendering an HTML page and wants to include a reference to a private object (eg <img src='...'>, it generates a pre-signed URL that grants temporary access to a private object
When the user's browser sends the URL to S3, the signature is verified and the expiry time is checked. If it is valid, then S3 returns the object.
This is common in applications like photo-sharing systems where users might want to share photos with other users, so that the security is more complex than simply looking at the directory where the image is stored.
Bottom line
If you are using IAM Users, then use Option 1 and take advantage of IAM Policy Variables to write one policy that will grant appropriate access to each user. However, consider carefully whether giving IAM User access to external people is acceptable within your security posture.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"StringEquals": {
"s3:prefix": [
"client-1"
],
"s3:delimiter": [
"/"
]
}
}
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"StringLike": {
"s3:prefix": [
"client-1/*"
]
}
}
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::my-bucket/client-1/*"
}
]
}

How to revoke a user session when using AWS SSO?

I'm currently managing a AWS SSO solution using it with AzureAD. For our use case we need to be able to revoke access/session of a user.
In AzureAD it's pretty simple, go to the user, block him, revoke its session. It's done, user needs to relog but he won't be able to do so.
In AWS SSO, it looks a bit harder, I can't seem to find a way to instantly revoke a session. I can disable its access, but once he has a session, even deleting the user/group from AWS SSO will not terminate the session.
This causes quite a problem as this is compliant to my security standards.
Any ideas?
Thanks people
An option is to put in place a temporary SCP on the AWS account to deny all actions for the Role session of the user as shown below:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UserRestrictions",
"Effect": "Deny",
"Action": "*",
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"aws:userId": [
"AROAEXAMPLEROLEID:alice#example.com"
]
}
}
}
]}
After a day or so (or the max role duration) you could remove the SCP. This is useful if you only have a single role session but in the scenario of an AWS SSO user, the user probably has access to multiple Roles across multiple AWS accounts. Rather than adding multiple SCPs you could add a SCP higher up in the organizational hierarchy that denies actions for all Role sessions for the user as shown below:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UserRestrictions",
"Effect": "Deny",
"Action": "*",
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"aws:userId": [
"*:alice#example.com"
]
}
}
}
]}

IAM policy to allow users to disable only their own MFA device with API/CLI

I want to enable MFA in an AWS organization and allow users to manage only their own MFA device. But I want the users to be able to manage the device via the API/CLI (and not using the AWS Console).
I found this policy in the AWS documentation, which is almost good, as it allow to create/enable/delete its own MFAdevice:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUsersToCreateEnableResyncDeleteTheirOwnVirtualMFADevice",
"Effect": "Allow",
"Action": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ResyncMFADevice",
"iam:DeleteVirtualMFADevice"
],
"Resource": [
"arn:aws:iam::account-id-without-hyphens:mfa/${aws:username}",
"arn:aws:iam::account-id-without-hyphens:user/${aws:username}"
]
},
{
"Sid": "AllowUsersToDeactivateTheirOwnVirtualMFADevice",
"Effect": "Allow",
"Action": [
"iam:DeactivateMFADevice"
],
"Resource": [
"arn:aws:iam::account-id-without-hyphens:mfa/${aws:username}",
"arn:aws:iam::account-id-without-hyphens:user/${aws:username}"
],
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": true
}
}
}
]
}
However, for device desactivation (iam:DeactivateMFADevice), it requires the condition aws:MultiFactorAuthPresent to be true. This is a sound security measure, the user can not disable MFA, without using MFA. But in this policy, when logged with MFA via API/CLI using STS and assuming a role, the resource "arn:aws:iam::account-id-without-hyphens:mfa/${aws:username}" does not represent the actual resource, as ${aws:username} is not present. I suppose that when using the AWS Console as a user, the boolean aws:MultiFactorAuthPresent can be true. But as far as I know, with API/CLI it is not possible to have both aws:MultiFactorAuthPresent==true and ${aws:username} present.
So, what policy could I write to authorize only the users to disable their device when using MFA, but when they assume a role ?
[disclaimer, this is to be used with the awless CLI]

AWS IAM Role in EC2 and access to S3 from JupyterHub

In JupyterHub, installed in an EC2 instance with an IAM role which allows access to a specific S3 bucket when I try to access a file in that bucket with this code:
s3nRdd = spark.sparkContext.textFile("s3n://bucket/file")
I get this error:
IllegalArgumentException: u'AWS Access Key ID and Secret Access Key
must be specified as the username or password (respectively) of a s3n
URL, or by setting the fs.s3n.awsAccessKeyId or
fs.s3n.awsSecretAccessKey properties (respectively).'
However, when I export the AWS access key id and secret access key in the kernel configuration having the same policy as that role, the read for that file succeeds.
As the best practice is to use IAM roles, why doesn't the EC2 role work in this situation?
--update--
The EC2 IAM role has these 2 policies:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1488892557621",
"Action": "s3:*",
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::<bucket_name>",
"arn:aws:s3:::<bucket_name>/*"
]
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "ec2:*",
"Effect": "Allow",
"Resource": "*"
},
{
"Sid": "Stmt1480684159000",
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": [
"*"
]
}
]
}
Also, I am using hadoop version 2.4.0 which doesn't support s3a protocol and updating is not an option.
S3n doesn't support IAM roles, and 2.4 is a very outdated version anyway. Not as buggy as 2.5 when it comes to s3n, but still less than perfect.
If you want to use IAM roles, you are going to have to switch to S3a, and yes, for you, that does mean upgrading Hadoop. sorry.
You must create a bucket policy to allow access from particular IAM roles. Since S3 doesn't trust the roles, the API just fallback and ask for access key.
Just add soemthing like this in your bucket policy, replace all the custom <> parameter with your own values.
{
"Version": "2012-10-17",
"Id": "EC2IAMaccesss",
"Statement": [{
"Sid": "MyAppIAMRolesAccess",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<acc_id>:role/<yourIAMroleName>"
]
},
"Action": [
"s3:ListBucket",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::<yourbucket>/*",
"arn:aws:s3:::<yourbucket>"
]
}
]
}
(updates)
Make sure you give proper policy to the EC2 IAM Roles, because IAM roles is very powerful, no Policy is attach to it out of the box. You must assign a policy, e.g. for minimal S3 access, add AWSS3ReadOnly policy to the roles.
You may encounter issues of spark problematic interaction with IAM roles. Please check the documentation on spark access through s3n:// schema. Otherwise, use s3a://

Amazon IAM permissions list only one bucket

Currently I have group with a policy that looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1449507915000",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::artmakeit",
"arn:aws:s3:::artmakeit/*"
]
},
{
"Sid": "ListAllBuckets",
"Action": "s3:ListAllMyBuckets",
"Effect": "Allow",
"Resource": "*"
}
]
}
So, the IAM user, my client, can access his bucket correctly. But I am wondering if I can only let him see his bucket and not the complete list of my buckets. Is there a way to achieve this? I guess I should change the ListAllBuckets permission, but I don't know what to write instead, any ideas?
If users intend to access their buckets via the AWS Management Console, then they require the ListAllBuckets permission.
It is not possible to 'hide' the complete bucket list when using the console -- either they see them all or none at all.
Access via other methods (eg the AWS Command-Line Interface (CLI) or an API call) does not require that this permission be assigned.