AWS IAM policy to allow Cloudformation actions - amazon-web-services

Originally asked on https://forums.aws.amazon.com/thread.jspa?messageID=825006#825006
I am trying to restrict certain actions to specific sources. For example, EC2 and Cloudformation should only be accessible from a certain source IP address. I am able to achieve this with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": ["ec2:*", "cloudformation:*"],
"Resource": "*",
"Condition": {
"NotIpAddress": { "aws:SourceIp": ["1.2.3.4"] }
}
}
]
}
This works great (assuming there is an allow on ec2 and cloudformation without condition for the user) in that I can now create / view EC2 instances from source IP 1.2.3.4 but get errors trying from other sources.
However, if I try create a Cloudformation stack that contains an EC2 instance, the RunInstance action inherits my user account, but not my source IP. I would like to update the condition to allow Cloudformation source to be excluded from the Deny. I have tried this:
"Condition": {
"NotIpAddress": { "aws:SourceIp": ["1.2.3.4"] },
"StringNotEquals": {"aws:SourceIp":"cloudformation.amazonaws.com" }
}
}
I have also tried 2 Allow statements on those actions, each statement only having 1 condition - IpAddress with the sourceIp and the 2nd allow statement with "StringEquals" : {"aws:SourceIp":"cloudformation.amazonaws.com"} to those actions. I still get an error when CloudFormation tries to execute RunInstances action.
Here's the CloudTrail event showing the failure:
{
"eventVersion": "1.05",
"userIdentity": {
"type": "AssumedRole",
"principalId": "REDACTED",
"arn": "REDACTED",
"accountId": "REDACTED",
"sessionContext": {
"attributes": {
"creationDate": "2018-01-19T07:47:40Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "REDACTED",
"arn": "arn:aws:iam::REDACTED",
"accountId": "REDACTED",
"userName": "REDACTED"
}
},
"invokedBy": "cloudformation.amazonaws.com"
},
"eventTime": "2018-01-19T08:10:39Z",
"eventSource": "ec2.amazonaws.com",
"eventName": "RunInstances",
"awsRegion": "REDACTED",
"sourceIPAddress": "cloudformation.amazonaws.com",
"userAgent": "cloudformation.amazonaws.com",
"errorCode": "Client.UnauthorizedOperation",
"errorMessage": "You are not authorized to perform this operation. Encoded authorization failure message: REDACTED",
"requestParameters": { <details of instance here> },
"responseElements": null,
"requestID": "REDACTED",
"eventID": "REDACTED",
"eventType": "AwsApiCall",
"recipientAccountId": "REDACTED"
}
I have tried string matches on aws:SourceIp, aws:SourceIpAddress, aws:UserAgent - How can I allow ec2 actions to cloudformation?
Thanks.

Related

How to switch roles on AWS Console while requiring sts:RoleSessionName?

I have two AWS accounts, A and B. I authenticate to account A using SAML, then to access account B I switch roles. The setup worked well until I tried to enforce that, when switching roles the users need to provide their AWS username as the RoleSessionName. When I require that, then switching roles using the AWS cli works fine, but switching roles in the AWS Console stops working.
Here's the role trust policy that works on both cli and console:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::84...10:root"
},
"Action": "sts:AssumeRole"
}
]
}
Enforcing that the RoleSessionName be the AWS username means to change the policy like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::84...10:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringLike": {
"sts:RoleSessionName": "${aws:username}"
}
}
}
]
}
Note: if instead of "sts:RoleSessionName": "${aws:username}" I do "sts:RoleSessionName": "*", then I can switch roles on the AWS console, but I can't find a way to figure out a pattern that includes the user name and works. I tried *${aws:username}, ${aws:username}*, and *${aws:username}*.
Once I enforce the sts:RoleSessionName condition on the role trust policy, I can no longer switch roles on the AWS Console. What I see in the CloudTrail events (only visible on us-east-1 region), is that switching the role in the AWS Console is recorded as 2 events: a SwitchRole followed by a AssumeRole.
When I switch roles in the AWS console with the first policy, I see the two events in CloudTrail.
Successful SwitchRole event:
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "ARO...BYP:me#email.com",
"arn": "arn:aws:sts::84...10:assumed-role/my-saml-role/me#email.com",
"accountId": "84...10"
},
"eventTime": "2022-12-21T19:34:55Z",
"eventSource": "signin.amazonaws.com",
"eventName": "SwitchRole",
"awsRegion": "us-east-1",
"sourceIPAddress": "4...0",
"userAgent": "Mozilla/5.0 ...",
"requestParameters": null,
"responseElements": {
"SwitchRole": "Success"
},
"additionalEventData": {
"RedirectTo": "https://us-east-1.console.aws.amazon.com/cloudtrail/home?region=us-east-1#/events?ReadOnly=false",
"SwitchTo": "arn:aws:iam::13...20:role/my-role"
},
"eventID": "ef759d28-37cb-4ece-af7d-3d7c5326691d",
"readOnly": false,
"eventType": "AwsConsoleSignIn",
"managementEvent": true,
"recipientAccountId": "84...10",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.2",
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "signin.aws.amazon.com"
}
}
Successful AssumeRole event:
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "ARO...BYP:me#email.com",
"arn": "arn:aws:sts::84...10:assumed-role/my-saml-role/me#email.com",
"accountId": "84...10",
"accessKeyId": "ASIA...OA",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AR...BYP",
"arn": "arn:aws:sts::84...10:assumed-role/my-saml-role",
"accountId": "84...10",
"userName": "my-saml-role"
},
"webIdFederationData": {},
"attributes": {
"creationDate": "2022-12-21T19:10:25Z",
"mfaAuthenticated": "false"
}
}
},
"eventTime": "2022-12-21T19:34:55Z",
"eventSource": "sts.amazonaws.com",
"eventName": "AssumeRole",
"awsRegion": "us-east-1",
"sourceIPAddress": "4...0",
"userAgent": "AWS Signin, aws-internal/3 aws-sdk-java/1.12.339 Linux/5.4.215-mr.86.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.352-b09 java/1.8.0_352 kotlin/1.3.72 vendor/Oracle_Corporation cfg/retry-mode/standard",
"requestParameters": {
"roleArn": "arn:aws:iam::13...20:role/my-role",
"roleSessionName": "me#email.com"
},
"responseElements": {
"credentials": {
"accessKeyId": "AS...FJ",
"sessionToken": "IQ...VMRCMyvsQ==",
"expiration": "Dec 21, 2022, 8:34:55 PM"
},
"assumedRoleUser": {
"assumedRoleId": "AR...PX:me#email.com",
"arn": "arn:aws:sts::13...20:assumed-role/my-role/me#email.com"
}
},
"requestID": "6d9b977a-e489-4261-97b5-48c0167d82ea",
"eventID": "1f7178bd-046b-4e20-b264-dd86b8753a45",
"readOnly": true,
"resources": [
{
"accountId": "13...20",
"type": "AWS::IAM::Role",
"ARN": "arn:aws:iam::13...20:role/my-role"
}
],
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "84...10",
"sharedEventID": "e49297fc-883e-4160-86bc-d157bc58a3ee",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.2",
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "sts.us-east-1.amazonaws.com"
}
}
When I enforce the sts:RoleSessionName requirement, then the SwitchRole event fails with error switchrole.error.invalidparams.
Failed SwitchRole event
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AR..YP:me#email.com",
"arn": "arn:aws:sts::84...10:assumed-role/my-saml-role/me#email.com",
"accountId": "84...10"
},
"eventTime": "2022-12-21T19:25:23Z",
"eventSource": "signin.amazonaws.com",
"eventName": "SwitchRole",
"awsRegion": "us-east-1",
"sourceIPAddress": "4...0",
"userAgent": "Mozilla/5.0 ...",
"errorMessage": "switchrole.error.invalidparams",
"requestParameters": null,
"responseElements": {
"SwitchRole": "Failure"
},
"additionalEventData": {
"RedirectTo": "https://eu-central-1.console.aws.amazon.com/cloudtrail/home?region=eu-central-1#/events?ReadOnly=false",
"SwitchTo": "arn:aws:iam::13..20:role/my-role"
},
"eventID": "76de870e-7e75-43a9-a908-d34616022553",
"readOnly": false,
"eventType": "AwsConsoleSignIn",
"managementEvent": true,
"recipientAccountId": "84...10",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.2",
"cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
"clientProvidedHostHeader": "signin.aws.amazon.com"
}
}
The AWS docs mentions that sts:RoleSessionName is available when assuming a role in the AWS Console.
sts:RoleSessionName Works with string operators.
Use this key to compare the session name that a principal specifies
when assuming a role with the value that is specified in the policy.
Availability – This key is present in the request when the principal
assumes the role using the AWS Management Console, any assume-role CLI
command, or any AWS STS AssumeRole API operation.
Src: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_iam-condition-keys.html#condition-keys-sts

Determine the exact problem point of calling AWS Backup to AWS KMS in order to decrypt S3 bucket

Here is a log in AWS CloudTrail presumably indicating that
the AWS Backup service ("userAgent": "backup.amazonaws.com",)
tries to call the AWS KMS service("eventSource": "kms.amazonaws.com",)
in order to decrypt("eventName": "Decrypt",)
some S3 bucket("principalId": "AROAY6JAXY37VKF726QCA:AWS_BACKUP_S3_71C3048C",) and fails.
But there are no clues as to which particular bucket or KMS key caused the problem, I'd appreciate any advice on how to determine the exact problem point, thanks.
BTW the account contains hundreds of keys and buckets that are being back-up, so to determine the problem point in some manual way like, like researching each bucket/key, is very undesirable:)
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAY6JAXY37VKF726QCA:AWS_BACKUP_S3_71C3048C",
"arn": "arn:aws:sts::****:assumed-role/AWSBackupDefaultServiceRole/AWS_BACKUP_S3_71C3048C",
"accountId": "****",
"accessKeyId": "ASIAY6JAXY37WHC7VFWN",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAY6JAXY37VKF726QCA",
"arn": "arn:aws:iam::****:role/service-role/AWSBackupDefaultServiceRole",
"accountId": "****",
"userName": "AWSBackupDefaultServiceRole"
},
"webIdFederationData": {},
"attributes": {
"creationDate": "2022-12-18T05:54:35Z",
"mfaAuthenticated": "false"
}
},
"invokedBy": "backup.amazonaws.com"
},
"eventTime": "2022-12-18T05:54:35Z",
"eventSource": "kms.amazonaws.com",
"eventName": "Decrypt",
"awsRegion": "eu-central-1",
"sourceIPAddress": "backup.amazonaws.com",
"userAgent": "backup.amazonaws.com",
"errorCode": "AccessDenied",
"errorMessage": "The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.",
"requestParameters": null,
"responseElements": null,
"requestID": "660b1b90-66d9-4a2b-9ab1-4af4e195b71f",
"eventID": "ef6fe50b-67f9-46c6-bbf5-057de88c3c1c",
"readOnly": true,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "****",
"eventCategory": "Management"
}

How to fix AWS Config generating AccessDenied error?

I am trying to allow AWS Config to write to a non-public S3 bucket.
Based on the official documentation, I should have two policies assigned to the AWS role. However, It is not possible to add any policy to the service-linked role, neither to create a custom new service-linked role for AWS config.
As such, how can I stop receiving the S3 AccessDenied error without making the bucket public?
edit: here is the error log:
{
"eventVersion": "1.07",
"userIdentity": {
"type": "AssumedRole",
"principalId": "xxxxxxxxxxxxxxxxxxxxx:AWSConfig-BucketConfigCheck",
"arn": "arn:aws:sts::xxxxxxxxxxxx:assumed-role/AWSServiceRoleForConfig/AWSConfig-BucketConfigCheck",
"accountId": "xxxxxxxxxxxx",
"accessKeyId": "xxxxxxxxxxxxxxxxxxxx",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "xxxxxxxxxxxxxxxxxxxxx",
"arn": "arn:aws:iam::xxxxxxxxxxxx:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
"accountId": "xxxxxxxxxxxx",
"userName": "AWSServiceRoleForConfig"
},
"attributes": {
"creationDate": "2020-04-30T00:43:57Z",
"mfaAuthenticated": "false"
}
},
"invokedBy": "AWS Internal"
},
"eventTime": "2020-04-30T00:43:57Z",
"eventSource": "s3.amazonaws.com",
"eventName": "PutObject",
"awsRegion": "eu-west-1",
"sourceIPAddress": "xxx.xxx.xxx.xxx",
"userAgent": "[AWSConfig]",
"errorCode": "AccessDenied",
"errorMessage": "Access Denied",
"requestParameters": {
"bucketName": "aws-config-bucket-xxxxxxxxxxxx",
"Host": "aws-config-bucket-xxxxxxxxxxxx.s3.eu-west-1.amazonaws.com",
"x-amz-acl": "bucket-owner-full-control",
"x-amz-server-side-encryption": "AES256",
"key": "AWSLogs/xxxxxxxxxxxx/Config/ConfigWritabilityCheckFile"
},
"responseElements": null,
"additionalEventData": {
"SignatureVersion": "SigV4",
"CipherSuite": "ECDHE-RSA-AES128-SHA",
"bytesTransferredIn": 0,
"AuthenticationMethod": "AuthHeader",
"x-amz-id-2": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=",
"bytesTransferredOut": 243
},
"requestID": "xxxxxxxxxxxxxxxx",
"eventID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"readOnly": false,
"resources": [
{
"type": "AWS::S3::Object",
"ARN": "arn:aws:s3:::aws-config-bucket-xxxxxxxxxxxx/AWSLogs/xxxxxxxxxxxx/Config/ConfigWritabilityCheckFile"
},
{
"accountId": "xxxxxxxxxxxx",
"type": "AWS::S3::Bucket",
"ARN": "arn:aws:s3:::aws-config-bucket-xxxxxxxxxxxx"
}
],
"eventType": "AwsApiCall",
"managementEvent": false,
"recipientAccountId": "xxxxxxxxxxxx",
"vpcEndpointId": "vpce-xxxxxxxx",
"eventCategory": "Data"
}
I found the answer here: https://forums.aws.amazon.com/thread.jspa?threadID=314156
When AWS Config sends configuration information to an Amazon S3
bucket in another account, it first attempts to use the IAM role, but
this attempt fails if the access policy for the bucket does not grant
WRITE access to the IAM role. In this event, AWS Config sends the
information again, this time as the AWS Config service principal.
I checked my logs and there was an AWS Config service principal log, the same second as the AccessDenied, that was being accepted. Therefore, the error can be safely ignored. I have updated my Cloudwatch alarm to ignore it:
{($.errorCode="*UnauthorizedOperation") || (($.errorCode="AccessDenied*") && (($.userIdentity.type!="AssumedRole") || ($.userAgent!="[AWSConfig]")))}

Create CloudWatch Alarm to notify about setting a S3 object to public

I want to create on CloudWatch a metric filter and an alarm based on it to notify me about S3 events, specially when a file or a bucket is set to public. This is the metric filter I used to create the metric:
{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl)
|| ($.eventName = PutObjectAcl)) &&
(($.requestParameters.AccessControlPolicy.AccessControlList.Grant.Grantee.type
= Group ))}
I tested this pattern by putting the following Custom log data :
{
"Records": [
{
"eventVersion": "1.03",
"userIdentity": {
"type": "IAMUser",
"principalId": "111122223333",
"arn": "arn:aws:iam::111122223333:user/myUserName",
"accountId": "111122223333",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"userName": "myUserName"
},
"eventTime": "2015-08-26T20:46:31Z",
"eventSource": "s3.amazonaws.com",
"eventName": "DeleteBucketPolicy",
"awsRegion": "us-west-2",
"sourceIPAddress": "127.0.0.1",
"userAgent": "[]",
"requestParameters": {
"bucketName": "myawsbucket"
},
"responseElements": null,
"requestID": "47B8E8D397DCE7A6",
"eventID": "cdc4b7ed-e171-4cef-975a-ad829d4123e8",
"eventType": "AwsApiCall",
"recipientAccountId": "111122223333"
},
{
"eventVersion": "1.03",
"userIdentity": {
"type": "IAMUser",
"principalId": "111122223333",
"arn": "arn:aws:iam::111122223333:user/myUserName",
"accountId": "111122223333",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"userName": "myUserName"
},
"eventTime": "2015-08-26T20:46:31Z",
"eventSource": "s3.amazonaws.com",
"eventName": "PutBucketAcl",
"awsRegion": "us-west-2",
"sourceIPAddress": "",
"userAgent": "[]",
"requestParameters": {
"bucketName": "",
"AccessControlPolicy": {
"AccessControlList": {
"Grant": {
"Grantee": {
"xsi:type": "Group",
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"ID": "d25639fbe9c19cd30a4c0f43fbf00e2d3f96400a9aa8dabfbbebe1906Example"
},
"Permission": "FULL_CONTROL"
}
},
"xmlns": "http://s3.amazonaws.com/doc/2006-03-01/",
"Owner": {
"ID": "d25639fbe9c19cd30a4c0f43fbf00e2d3f96400a9aa8dabfbbebe1906Example"
}
}
},
"responseElements": null,
"requestID": "BD8798EACDD16751",
"eventID": "607b9532-1423-41c7-b048-ec2641693c47",
"eventType": "AwsApiCall",
"recipientAccountId": "111122223333"
},
{
"eventVersion": "1.03",
"userIdentity": {
"type": "IAMUser",
"principalId": "111122223333",
"arn": "arn:aws:iam::111122223333:user/myUserName",
"accountId": "111122223333",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"userName": "myUserName"
},
"eventTime": "2015-08-26T20:46:31Z",
"eventSource": "s3.amazonaws.com",
"eventName": "GetBucketVersioning",
"awsRegion": "us-west-2",
"sourceIPAddress": "",
"userAgent": "[]",
"requestParameters": {
"bucketName": "myawsbucket"
},
"responseElements": null,
"requestID": "07D681279BD94AED",
"eventID": "f2b287f3-0df1-4961-a2f4-c4bdfed47657",
"eventType": "AwsApiCall",
"recipientAccountId": "111122223333"
}
]
}
I clicked Test Pattern and I get this message:
Results Found 0 matches out of 50 event(s) in the sample log.
Is the metric filter proper and correct ? I'm supposed to have one result but it is not coming up.
Calculating whether a policy is providing open access is quite complex, due to the many ways that rules can be specified in the Bucket Policy (for example, wildcards can provide access).
An easier approach would be to use the Amazon S3 Bucket Permissions check in Trusted Advisor:
Checks buckets in Amazon Simple Storage Service (Amazon S3) that have open access permissions or allow access to any authenticated AWS user.
You can then Monitor Trusted Advisor Check Results with Amazon CloudWatch Events.
However, that particular check is not included in the Free Tier for Trusted Advisor. You would need to be on a Support Plan for that check to operate.
The Amazon S3 console was also recently updated -- it now clearly shows any buckets with public permissions.

Cannot access S3 bucket from Lambda function

I created a simple Lambda function that receives a file as a Base64 string and uploads it to my S3 bucket. I used the default S3 role suggested by the Lambda console:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::*"
]
}
]
}
But I'm still getting an access error:
{
"errorMessage": "The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.",
"errorType": "PermanentRedirect",
"stackTrace": [
Doesn't the "arn:aws:s3:::*" cover all my needs? What else do I need to add to use this function?
The must be addressed using the specified endpoint error normally indicates a mismatch between the bucket region and the endpoint that you are calling with your code.
For example: The AWS client connection is established with Sydney but the bucket is in Tokyo.
Try something like this:
var s3 = new AWS.S3({region: 'ap-southeast-2'});
It might also happens to your sourceKey settings, try to add the exact file path in your S3 like:
{
"Records": [
{
"eventVersion": "2.0",
"eventTime": "1970-01-01T00:00:00.000Z",
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"s3": {
"configurationId": "testConfigRule",
"object": {
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901",
"key": "images/HappyFace.jpg",
"size": 1024
},
"bucket": {
"arn": "arn:aws:s3:::mybucket",
"name": "mybucket",
"ownerIdentity": {
"principalId": "EXAMPLE"
}
},
"s3SchemaVersion": "1.0"
},
"responseElements": {
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
"x-amz-request-id": "EXAMPLE123456789"
},
"awsRegion": "us-east-1",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"eventSource": "aws:s3"
}
]
}