-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
When using Moto with IAM authorization enabled, AccessDenied errors are produced in situations where they should be allowed. The particular scenario that this bug report pertains to is due to the default behavior when BaseResponse._determine_resource is not implemented for a particular resource (which seems to be just about all of them).
Here is a script that reproduces the issue using an SQS queue.
import json
import boto3
from moto import mock_aws
from moto.core import enable_iam_authentication
@mock_aws
def test_moto_arn_matching_bug():
sqs = boto3.resource("sqs")
queue = sqs.create_queue(QueueName="test-queue")
queue_arn = queue.attributes['QueueArn']
iam = boto3.client("iam")
role_arn = iam.create_role(
RoleName="test-role",
AssumeRolePolicyDocument=json.dumps(dict(
Version="2012-10-17",
Statement=[
dict(Effect="Allow", Principal=dict(AWS="*"), Action="sts:AssumeRole")
]
))
)["Role"]["Arn"]
policy_arn = iam.create_policy(
PolicyName="test-policy",
PolicyDocument=json.dumps(dict(
Version="2012-10-17",
Statement=[
dict(Effect="Allow", Action="sqs:*", Resource=queue_arn)
]
))
)["Policy"]["Arn"]
iam.attach_role_policy(
RoleName="test-role",
PolicyArn=policy_arn
)
sts = boto3.client("sts")
crendentials = sts.assume_role(RoleArn=role_arn, RoleSessionName="test-session")["Credentials"]
with enable_iam_authentication():
restriced_session = boto3.Session(aws_access_key_id=crendentials["AccessKeyId"],
aws_secret_access_key=crendentials["SecretAccessKey"],
aws_session_token=crendentials["SessionToken"])
restricted_queue = restriced_session.resource("sqs").Queue(queue.url)
restricted_queue.send_message(MessageBody="Test message with IAM auth")
if __name__ == "__main__":
test_moto_arn_matching_bug()The final call to restricted_queue.send_message should succeed, but instead fails with AccessDenied. The behavior is such that the Allow policy does not match the queue's ARN.
This is because of the code in BaseResponse.call_action
def call_action(self) -> TYPE_RESPONSE:
headers = self.response_headers
if hasattr(self, "_determine_resource"):
resource = self._determine_resource()
else:
resource = "*"This causes the resource to default to * which does not match the Allowed ARN in the policy.
I suspect that _determine_resource needs to be implemented for all BaseResponse child classes in order to address this problem.