Question:
Is it possible to check if a particular AWS IAM key has permissions for a set of specific commands?
Essentially, is there an API for AWS’s privacy simulator?
So far I’ve been using hacks, such as executing a command with incorrect parameters that utilizes the permission in question, and watching what response I get back.
Example:
1 2 3 4 5 6 7 8 9 10 |
# needed resource: 'elasticloadbalancer:SetLoadBalancerListenerSSLCertificate' # Check: try: elb.set_listener_SSL_certificate(443, 'fake') except BotoServerError as e: if e.error_code == 'AccessDenied': print ("You don't have access to " "elasticloadbalancer:SetLoadBalancerListenerSSLCertificate") |
This is obviously hacky. Ideally I’d have some function call like iam.check_against(resource)
or something. Any suggestions?
Answer:
See boto3’s simulate_principal_policy.
I’ve made this function to test for permissions (you’ll need to modify it slightly, as it’s not completely self-contained):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from typing import Dict, List, Optional def blocked( actions: List[str], resources: Optional[List[str]] = None, context: Optional[Dict[str, List]] = None ) -> List[str]: """test whether IAM user is able to use specified AWS action(s) Args: actions (list): AWS action(s) to validate IAM user can use. resources (list): Check if action(s) can be used on resource(s). If None, action(s) must be usable on all resources ("*"). context (dict): Check if action(s) can be used with context(s). If None, it is expected that no context restrictions were set. Returns: list: Actions denied by IAM due to insufficient permissions. """ if not actions: return [] actions = list(set(actions)) if resources is None: resources = ["*"] _context: List[Dict] = [{}] if context is not None: # Convert context dict to list[dict] expected by ContextEntries. _context = [{ 'ContextKeyName': context_key, 'ContextKeyValues': [str(val) for val in context_values], 'ContextKeyType': "string" } for context_key, context_values in context.items()] # You'll need to create an IAM client here results = aws.iam_client().simulate_principal_policy( PolicySourceArn=consts.IAM_ARN, # Your IAM user's ARN goes here ActionNames=actions, ResourceArns=resources, ContextEntries=_context )['EvaluationResults'] return sorted([result['EvalActionName'] for result in results if result['EvalDecision'] != "allowed"]) |
You need to pass the permission’s original action names to
actions
, like so:
1 2 3 4 5 6 7 8 9 |
blocked_actions = verify_perms.blocked(actions=[ "iam:ListUsers", "iam:ListAccessKeys", "iam:DeleteAccessKey", "iam:ListGroupsForUser", "iam:RemoveUserFromGroup", "iam:DeleteUser" ]) |
Here’s an example that uses the
resources
and context
arguments as well:
1 2 3 4 5 6 7 8 9 10 11 |
def validate_type_and_size_allowed(instance_type, volume_size): """validate user is allowed to create instance with type and size""" if validate_perms.blocked(actions=["ec2:RunInstances"], resources=["arn:aws:ec2:*:*:instance/*"], context={'ec2:InstanceType': [instance_type]}): halt.err(f"Instance type {instance_type} not permitted.") if validate_perms.blocked(actions=["ec2:RunInstances"], resources=["arn:aws:ec2:*:*:volume/*"], context={'ec2:VolumeSize': [volume_size]}): halt.err(f"Volume size {volume_size}GiB is too large.") |