Identity and Access Management (IAM)

Identity and Access Management (IAM)

By default, LocalStack uses not enforce security policies for client requests. In LocalStack Pro, the IAM security enforcement feature can be used to test your security policies and create a more realistic environment that more closely resembles real AWS.

Note: The environment configuration ENFORCE_IAM=1 is required to enable this feature. (By default, IAM enforcement is disabled, and all APIs can be accessed without authentication.)

Below is a simple example that illustrates the use of IAM policy enforcement. It first attempts to create an S3 bucket with the default user (which fails), then create a user and attempts to create a bucket with that user (which fails again), and then finally attaches a policy to the user to allow s3:CreateBucket, which allows the bucket to be created.

  1. $ awslocal s3 mb s3://test
  2. make_bucket failed: s3://test An error occurred (AccessDeniedException) when calling the CreateBucket operation: Access to the specified resource is denied
  3. $ awslocal iam create-user --user-name test
  4. ...
  5. $ awslocal iam create-access-key --user-name test
  6. ...
  7. "AccessKeyId": "AKIA4HPFP0TZHP3Z5VI6",
  8. "SecretAccessKey": "mwi/8Zhg8ypkJQmkdBq87UA3MbSa3x0HWnkcC/Ua",
  9. ...
  10. $ export AWS_ACCESS_KEY_ID=AKIA4HPFP0TZHP3Z5VI6 AWS_SECRET_ACCESS_KEY=mwi/8Zhg8ypkJQmkdBq87UA3MbSa3x0HWnkcC/Ua
  11. $ awslocal s3 mb s3://test
  12. make_bucket failed: s3://test An error occurred (AccessDeniedException) when calling the CreateBucket operation: Access to the specified resource is denied
  13. $ awslocal iam create-policy --policy-name p1 --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:CreateBucket","Resource":"*"}]}'
  14. ...
  15. $ awslocal iam attach-user-policy --user-name test --policy-arn arn:aws:iam::000000000000:policy/p1
  16. $ awslocal s3 mb s3://test
  17. make_bucket: test

Please notice that by default if you do not have valid credentials representing a user or assumed role, LocalStack will identify requests as coming from the root user.

Note: Credentials are currently extracted in the request but signature is not validated - exceptions apply for s3 presigned URLs, for example.

For example:

  1. $ awslocal sts get-caller-identity
  2. {
  3. "UserId": "AKIAIOSFODNN7EXAMPLE",
  4. "Account": "000000000000",
  5. "Arn": "arn:aws:iam::000000000000:root"
  6. }
  7. $ awslocal iam create-user --user-name test
  8. ...
  9. $ awslocal iam create-access-key --user-name test
  10. ...
  11. "AccessKeyId": "AKIA4HPFP0TZHP3Z5VI6",
  12. "SecretAccessKey": "mwi/8Zhg8ypkJQmkdBq87UA3MbSa3x0HWnkcC/Ua",
  13. ...
  14. $ export AWS_ACCESS_KEY_ID=AKIA4HPFP0TZHP3Z5VI6 AWS_SECRET_ACCESS_KEY=mwi/8Zhg8ypkJQmkdBq87UA3MbSa3x0HWnkcC/Ua
  15. $ awslocal sts get-caller-identity
  16. {
  17. "UserId": "b2yxf5g824zklfx5ry8o",
  18. "Account": "000000000000",
  19. "Arn": "arn:aws:iam::000000000000:user/test"
  20. }

Explainable IAM

Since 1.0, our policy engine logs output related to failed policy evaluation to the LocalStack log. There, you can see which additional policies are necessary for your request to succeed. You need to enable DEBUG=1 to see these log messages.

For example, let us try to add a policy for creating Lambda functions, but only pass lambda:CreateFunction as allowed action - iam:PassRole (which is also required) is missing:

Our policy document policy_1.json:

  1. {
  2. "Version": "2012-10-17",
  3. "Statement": [
  4. {
  5. "Sid": "FirstStatement",
  6. "Effect": "Allow",
  7. "Action": "lambda:CreateFunction",
  8. "Resource": "*"
  9. }
  10. ]
  11. }

Let us create a new user, put the policies, create access keys, and try to create a function with the user:

  1. $ awslocal iam create-user --user-name test-user
  2. {
  3. "User": {
  4. "Path": "/",
  5. "UserName": "test-user",
  6. "UserId": "x8a2eu4mc91yqtjazvhp",
  7. "Arn": "arn:aws:iam::000000000000:user/test-user",
  8. "CreateDate": "2022-07-05T16:08:25.741000+00:00"
  9. }
  10. }
  11. $ awslocal iam put-user-policy --user-name test-user --policy-name policy1 --policy-document file://policy_1.json
  12. $ awslocal iam create-access-key --user-name test-user
  13. $ export AWS_ACCESS_KEY_ID=...
  14. $ export AWS_SECRET_ACCESS_KEY=...
  15. $ awslocal lambda create-function --function-name test-function --role arn:aws:iam::000000000000:role/lambda-role --runtime python3.8 --handler handler.handler --zip-file fileb://function.zip
  16. An error occurred (AccessDeniedException) when calling the CreateFunction operation: Access to the specified resource is denied

When looking in the LocalStack logs, we can now see 5 log entries specific to that (denied) request:

  1. INFO:localstack_ext.services.iam.policy_engine.handler: Request for service lambda for operation CreateFunction denied.
  2. DEBUG:localstack_ext.services.iam.policy_engine.handler: Necessary permissions for this action: ["Action 'lambda:CreateFunction' for 'arn:aws:lambda:us-east-1:000000000000:function:test-function'", "Action 'iam:PassRole' for 'arn:aws:iam::000000000000:role/lambda-role'"]
  3. DEBUG:localstack_ext.services.iam.policy_engine.handler: 0 permissions have been explicitly denied: []
  4. DEBUG:localstack_ext.services.iam.policy_engine.handler: 1 permissions have been explicitly allowed: ["Action 'lambda:CreateFunction' for 'arn:aws:lambda:us-east-1:000000000000:function:test-function'"]
  5. DEBUG:localstack_ext.services.iam.policy_engine.handler: 1 permissions have been implicitly denied: ["Action 'iam:PassRole' for 'arn:aws:iam::000000000000:role/lambda-role'"]

So we can see the action iam:PassRole is not allowed but implicitly denied (meaning there is no explicit deny statement in the applicable policies, but now allow either) for your user for resource arn:aws:iam::000000000000:role/lambda-role. If we now add this to our policy (since it is an example let’s do it very simple with the same wildcard resource):

  1. {
  2. "Version": "2012-10-17",
  3. "Statement": [
  4. {
  5. "Sid": "FirstStatement",
  6. "Effect": "Allow",
  7. "Action": ["lambda:CreateFunction", "iam:PassRole"],
  8. "Resource": "*"
  9. }
  10. ]
  11. }

the call is correctly executed.

Soft Mode

If you enable IAM_SOFT_MODE=1, you can look at the logs whether your requests would have been denied or not, while still being able to execute your whole stack without interference. This is especially useful when trying to find missing permissions over a whole stack (with resources depending on each other) at a time without having to redeploy for every missing permission.

Note: As of 1.0, resource based policies and conditions are not yet supported. Please try keeping to identity-based policies where possible. Inter-service communication evaluation (for example for sts:AssumeRole) also is not supported, which currently reduces the impact of those missing features.

Supported APIs

IAM security enforcement is available for all the AWS APIs of LocalStack - it has been tested, among others, for the following services: ACM, API Gateway, CloudFormation, CloudWatch (metrics/events/logs), DynamoDB, DynamoDB Streams, Elasticsearch Service, EventBus, Kinesis, KMS, Lambda, Redshift, S3, SecretsManager, SNS, SQS.

Last modified July 26, 2022: fix some typos (#214) (6ab8502d)