create AWS IAM Policy using cloudformation - amazon-web-services

I have an IAM role(MyIAMrole) which has already been created. I want to attach a policy to this role using a Cloudformation template.
"Mypolicy":{
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "assume-role-policy",
"PolicyDocument": {
"Version" : "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "*" }
]
},
"Roles": [ { "Ref": "arn:aws:iam::*:role/MyIAMrole" } ]
}
}
When I try to validate this I am getting an error saying "Unreolved reference options".
How to attach this policy to an already existing role?

I managed to get your code snippet to work by referring to the Name of a role rather than the ARN.
As per the AWS::IAM::Policy documentation:
Roles: The names of AWS::IAM::Roles to which this policy will be attached.
However, while the stack went to CREATE_COMPLETE, I couldn't see the policy listed in the Policies section of IAM, nor could I see the policy attached to the referenced role.
It might be that you cannot use CloudFormation to attach a policy to an existing role. You might need to create the Role as part of the CloudFormation template to be able to attach a role.

Related

Official example on AWS documentation on how to add a service principal on a resource based policy does not work

I several lambda functions on my account to be able to access a secret.
(I cannot use identity policies, don't ask why)
I am following this example from the official documentation so I am creating this resource based policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*",
"Condition": {
"ArnLike": {
"aws:sourceArn": "arn:aws:lambda::1234567891911:*"
},
"StringEquals": {
"aws:sourceAccount": "1234567891911"
}
}
}
]
}
My lambda invocation fails as follows:
"An error occurred (AccessDeniedException) when calling the GetSecretValue operation: User: arn:aws:sts::1234567891911:assumed-role/my-secret-name/my-lambda-name is not authorized to perform: secretsmanager:GetSecretValue on resource: ps-shield-token because no identity-based policy allows the secretsmanager:GetSecretValue action",
????
I don't see the problem. Your policy example is valid for services that support service-linked roles1. Lambda functions do not support service-linked roles. Therefore, the policy example is not valid for Lambda.
Service-linked roles, which are AWS-managed, are referenced by service name in resource-based policies, as in the OP. For instance, the principal { “Service”: “elasticloadbalancing.amazonaws.com” } refers to the AWS-managed ELB service-linked-role, which is called AWSServiceRoleForElasticLoadBalancing. Again, there's no equivalent lambda.amazon.aws option here, because Lambda has no service-linked role2.
Functions have user-managed execution roles. Execution roles (EC2 Instances and ECS Tasks have something similar) are referenced by the role ARN in the resource-based policy "Principal": { AWS: <Lambda Role Arn> }, as in #jellycsc's answer.
Although the docs could definitely be clearer, your Example: Service principal does refer to just to service-linked roles. The first link on the page, AWS Service Principal, refers to "service principal" as used "services that support service-linked roles".
Lambda#Edge does support service-linked roles.
It's not the lambda service that's getting the secret value. The lambda service first assumes the execution role which you set in your lambda function, and the execution role is the principle of the secretsmanager:GetSecretValue action. Therefore, the following policy should work.
{
"Version": "2012-10-17",
"Statement":
[
{
"Effect": "Allow",
"Principal":
{
"AWS": "arn:aws:iam::1234567891911:role/<lambda-execution-role-name>"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}

Why does restricting `ssm:sendCommand` to a specific document via an IAM policy show access denied?

I'm trying to have an IAM user who can only use SSM Run Command with a specific Document.
If I have the following policy attached to the user, that user can indeed only successfully execute AWS-RunShellScript (which is an AWS managed) document on EC2 instances.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:DescribeDocument",
"ssm:DescribeDocumentParameters",
"ssm:DescribeDocumentPermission",
"ssm:GetCommandInvocation",
"ssm:GetDocument",
"ssm:ListCommandInvocations",
"ssm:ListCommands",
"ssm:ListDocumentMetadataHistory",
"ssm:ListDocuments",
"ssm:ListDocumentVersions"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ssm:SendCommand",
"Resource": "arn:aws:ssm:us-west-2:999999999999:document/AWS-RunShellScript"
}
]
}
However, if I replace the resource item in the policy with a custom document ARN that I created (e.g. arn:aws:ssm:us-west-2:999999999999:document/CustomDocument), I get "Access Denied"
It's not really clear in the documentation but to limit ssm:SendCommand, you must use the Resource field to specify both what document(s) the IAM user is allowed to run and what instance(s) you allow commands to be run on.
The user would see all instances when trying to run a command but will only be able to execute commands for the EC2 instance IDs specified in the IAM policy.
You can specify the policy to allow any instance like below if needs be or specify a limited list of instance IDs in line with the standard security advice of granting least privilege in IAM policies.
As to why the AWS managed document works, it's probably some internal magic 🤷‍♂️
This should work:
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":"ssm:SendCommand",
"Resource":[
"arn:aws:ec2:us-west-2:999999999999:instance/*",
"arn:aws:ssm:us-west-2:999999999999:document/AWS-RunShellScript"
]
}
]
}
Slightly similar unexpected behaviour (not really clear in the documentation either): if you define 2 blocks for the same action, they are considered by AWS as just one, eg:
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":"ssm:SendCommand",
"Resource":[
"arn:aws:ec2:us-west-2:999999999999:instance/i-1",
"arn:aws:ssm:us-west-2:999999999999:document/AWS-RunShellScript"
]
},
{
"Effect":"Allow",
"Action":"ssm:SendCommand",
"Resource":[
"arn:aws:ec2:us-west-2:999999999999:instance/i-2",
"arn:aws:ssm:us-west-2:999999999999:document/AWS-SecondDoc"
]
}
]
}
will allow you to connect to i-1 with both "AWS-RunShellScript" and "AWS-SecondDoc" (and same for i-2).

What is the ARN of an assumed role assumed by a Lambda function?

I am trying to use the NotPrincipal element in my bucket policy to explicitly deny access to my s3 bucket while whitelisting a particular lambda that accesses the bucket. I specified the role ARN and assumed role ARN for the lambda's role in the NotPrincipal element:
"arn:aws:iam::{Account ID}:role/service-role/{Lambda role name}",
"arn:aws:sts::{Account ID}:assumed-role/{Lambda role name}/{role session name}"
This doc explains the structure of the assumed role ARNs:
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids
I can't seem to get the assumed role ARN correct. The bucket policy is valid, but it seems I can provide anything for the role session name (the last part of the assumed-role ARN), and the ARN is considered valid. What does AWS set this role session name to when Lambda or other service assumes a service role? Is it possible to list active sessions for a role or list the assumed-role ARNs? I am currently using the Lambda function name for the role session name, but this is not working (the Lambda still cannot access the bucket).
Since I can't use wildcards in the NotPrincipal element, I need the full assumed-role ARN of the Lambda once it assumes the role.
UPDATE:
I tried using two conditions to deny all requests where the ARN does not match the ARN of the Lambda role or assumed role. The Lambda role is still denied from writing to S3 using the IAM policy simulator. Here is the policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WhitelistRegistryAPILambdaRole",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObjectVersion",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::{bucket name}",
"arn:aws:s3:::{bucket name}/*"
],
"Condition": {
"ArnNotLike": {
"AWS:SourceARN": "arn:aws:iam::{account ID}:role/{lambda role name}"
}
}
},
{
"Sid": "WhitelistRegistryAPILambdaAssumedRole",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObjectVersion",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::{bucket name}",
"arn:aws:s3:::{bucket name}/*"
],
"Condition": {
"ArnNotLike": {
"AWS:SourceARN": "arn:aws:sts::{account ID}:assumed-role/{lambda role name}/{lambda function name}"
}
}
}
]
}
TL;DR:
The Assumed Role ARN of a Lambda Function is constructed as this:
arn:aws:sts::{AccountID}:assumed-role/{RoleName}/{FunctionName}
Details:
So the "role session name" is, in your case, the lambda function name.
You can easily verify this, by trying to call an API from your Lambda (DynamoDB ListTables for example) for which you do not have permissions. The error message in the callback will also contain the assumed role ARN (note that some service such as S3 do not provide detailed error messages when an operation is denied. DynamoDB, Lambda, and most of the recently launched services, will.)
I'm not sure to understand why you need a NotPrincipal, as probably there is a better way to handle the scenario you described :) More info would be useful to provide a more precise answer.
From the AWS IAM Documentation:
Important: Very few scenarios require the use of NotPrincipal, and we
recommend that you explore other authorization options before you
decide to use NotPrincipal.

How to change s3 bucket policies with cloudformation?

I would like to be able to change the policies on s3 buckets using cloudformation. However when I attempt to do this I encounter the error:
2017-12-21 18:49:10 UTC TestBucketpolicyAwsS3Bucketpolicy CREATE_FAILED API: s3:PutBucketPolicy Access Denied
Here is an example of a cloudformation template that fails due to this issue:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "",
"Resources": {
"TestBucketpolicyAwsS3Bucketpolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": "alex-test-bucket-123",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Resource": "arn:aws:s3:::alex-test-bucket-123/*",
"Action": [
"s3:GetObject*",
"s3:DeleteObject*",
"s3:PutObject*"
]
}
]
}
}
}
}
}
I have tried changing policies on both my IAM user and the actual bucket I want to manage with cloudformation, but neither solution has resolved the issue. How can I get remove this "s3:PutBucketPolicy" restriction?
Edit: I think the issue may be that only IAM roles can access the "s3:PutBucketPolicy" operation. I may need to create a role with s3 access then establish a trust relationship with the user that runs this cloudformation template.
https://docs.aws.amazon.com/cli/latest/userguide/cli-roles.html
IAM users cannot directly run s3:PutBucketPolicy operations. You need to create a separate IAM role and attach it to your user with a trust relationship to assume that IAM role.
Your role will need s3 and cloudformation access. The policy document below will work.
{
"Version": "2012-10-17",
"Statement": {
"Action": [
"s3:*",
"cloudformation:*"
],
"Resource": "*",
"Effect": "Allow"
}
}
The arn of your IAM role will then need to be set in your config or the AWS_STS_ROLE_ARN environmental variable along with your aws access keys.
Once you assume the role you will then be able to change s3 bucket policies.
Note that this will override any permissions your user has when you set your AWS_STS_ROLE_ARN in your config or environmental variables.

AWS::IAM::Policy for any user

Is there any way to attach some policy for any user in cloudfromation? When you create a stack via aws js you can pass stack policy document with Principal as "*". But if you create a AWS::IAM::Policy inside cloudformation template, you must provide Role, User or Group, and "*" doesn't work.
Or how can I attach policy document for nested stack?
Nested CloudFormation stacks use the same stack policy document as the parent stack, which you specify in the AWS Console or via the API when creating/updating a stack (e.g., using the AWS SDK for JS, as you mentioned). As documented, for stack policy documents the Principal element is required, but supports only the wild card (*).
You can further restrict actions on nested stacks by adding statements with a Condition where ResourceType equals AWS::CloudFormation::Stack, as outlined in the Prevent Updates to Nested Stacks example in the documentation:
{
"Statement" : [
{
"Effect" : "Deny",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "*",
"Condition" : {
"StringEquals" : {
"ResourceType" : ["AWS::CloudFormation::Stack"]
}
}
},
{
"Effect" : "Allow",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "*"
}
]
}
The AWS::IAM::Policy resource is unrelated to the specific use-case of specifying a policy for a nested stack, but for reference, AWS::IAM::Policy creates a Policy that you attach to Users or Groups, which do not allow the Principal element to be specified, as outlined in the documentation:
Do not use the Principal element in policies that you attach to IAM users and groups. Similarly, you do not specify a principal in the access policy for an IAM role. In those cases, the principal is implicitly the user that the policy is attached to (for IAM users) or the user who assumes the role (for role access policies). When the policy is attached to an IAM group, the principal is the IAM user in that group who is making the request.
What I do, is trust relationship policy to the same (or different) account. It says root but is actually applied to the whole account:
{
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123ACCNO4567:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}