Skip to main content

PolicyUniverse

Version

Build Status

Coverage Status

Code style: black

This package provides classes to parse AWS IAM and Resource Policies.

Additionally, this package can expand wildcards in AWS Policies using permissions obtained from the AWS Policy Generator.

See the Service and Permissions data.

This package can also minify an AWS policy to help you stay under policy size limits. Avoid doing this if possible, as it creates ugly policies. 💩

Install:

pip install policyuniverse

Usage:

Reading ARNs

from policyuniverse.arn import ARN
arn = ARN('arn:aws:iam::012345678910:role/SomeTestRoleForTesting')
assert arn.error == False
assert arn.tech == 'iam'
assert arn.region == ''  # IAM is universal/global
assert arn.account_number == '012345678910'
assert arn.name == 'role/SomeTestRoleForTesting'
assert arn.partition == 'aws'
assert arn.root == False  # Not the root ARN
assert arn.service == False  # Not an AWS service like lambda.amazonaws.com

arn = ARN('012345678910')
assert arn.account_number == '012345678910'

arn = ARN('lambda.amazonaws.com')
assert arn.service == True
assert arn.tech == 'lambda'

IAM and Resource Policies

Policy with multiple statements

# Two statements, both with conditions
policy05 = dict(
    Version='2010-08-14',
    Statement=[
        dict(
            Effect='Allow',
            Principal='arn:aws:iam::012345678910:root',
            Action=['s3:*'],
            Resource='*',
            Condition={
                'IpAddress': {
                    'AWS:SourceIP': ['0.0.0.0/0']
                }}),
        dict(
            Effect='Allow',
            Principal='arn:aws:iam::*:role/Hello',
            Action=['ec2:*'],
            Resource='*',
            Condition={
                'StringLike': {
                    'AWS:SourceOwner': '012345678910'
                }})
        ])

from policyuniverse.policy import Policy
from policyuniverse.statement import ConditionTuple, PrincipalTuple

policy = Policy(policy05)
assert policy.whos_allowed() == set([
    PrincipalTuple(category='principal', value='arn:aws:iam::*:role/Hello'),
    PrincipalTuple(category='principal', value='arn:aws:iam::012345678910:root'),
    ConditionTuple(category='cidr', value='0.0.0.0/0'),
    ConditionTuple(category='account', value='012345678910')
])

# The given policy is not internet accessible.
# The first statement is limited by the principal, and the condition is basically a no-op.
# The second statement has a wildcard principal, but uses the condition to lock it down.
assert policy.is_internet_accessible() == False

Internet Accessible Policy:

# An internet accessible policy:
policy01 = dict(
    Version='2012-10-08',
    Statement=dict(
        Effect='Allow',
        Principal='*',
        Action=['rds:*'],
        Resource='*',
        Condition={
            'IpAddress': {
                'AWS:SourceIP': ['0.0.0.0/0']
            }
        }))

policy = Policy(policy01)
assert policy.is_internet_accessible() == True
assert policy.internet_accessible_actions() == set(['rds:*'])

Statements

A policy is simply a collection of statements.

statement12 = dict(
    Effect='Allow',
    Principal='*',
    Action=['rds:*'],
    Resource='*',
    Condition={
        'StringEquals': {
            'AWS:SourceVPC': 'vpc-111111',
            'AWS:Sourcevpce': 'vpce-111111',
            'AWS:SourceOwner': '012345678910',
            'AWS:SourceAccount': '012345678910'
        },
        'StringLike': {
            'AWS:userid': 'AROAI1111111111111111:*'
        },
        'ARNLike': {
            'AWS:SourceArn': 'arn:aws:iam::012345678910:role/Admin'
        },
        'IpAddressIfExists': {
            'AWS:SourceIP': [
                '123.45.67.89',
                '10.0.7.0/24',
                '172.16.0.0/16']
        }
    })

from policyuniverse.statement import Statement
from policyuniverse.statement import ConditionTuple, PrincipalTuple

statement = Statement(statement12)
assert statement.effect == 'Allow'
assert statement.actions == set(['rds:*'])

# rds:* expands out to ~88 individual permissions
assert len(statement.actions_expanded) == 88

assert statement.uses_not_principal() == False
assert statement.principals == set(['*'])
assert statement.condition_arns == set(['arn:aws:iam::012345678910:role/Admin'])
assert statement.condition_accounts == set(['012345678910'])
assert statement.condition_userids == set(['AROAI1111111111111111:*'])
assert statement.condition_cidrs == set(['10.0.7.0/24', '172.16.0.0/16', '123.45.67.89'])
assert statement.condition_vpcs == set(['vpc-111111'])
assert statement.condition_vpces == set(['vpce-111111'])
assert statement.is_internet_accessible() == False
assert statement.whos_allowed() == set([
    PrincipalTuple(category='principal', value='*'),
    ConditionTuple(category='cidr', value='123.45.67.89'),
    ConditionTuple(category='account', value='012345678910'),
    ConditionTuple(category='userid', value='AROAI1111111111111111:*'),
    ConditionTuple(category='vpc', value='vpc-111111'),
    ConditionTuple(category='arn', value='arn:aws:iam::012345678910:role/Admin'),
    ConditionTuple(category='cidr', value='172.16.0.0/16'),
    ConditionTuple(category='vpce', value='vpce-111111'),
    ConditionTuple(category='cidr', value='10.0.7.0/24')])

Action Categories

policy = {
        "Statement": [{
            "Action": ["s3:put*", "sqs:get*", "sns:*"],
            "Resource": "*",
            "Effect": "Allow"
          }]
      }

from policyuniverse.policy import Policy
p = Policy(policy)
for k, v in p.action_summary().items():
    print(k,v)
>>> ('s3', set([u'Write', u'Permissions', u'Tagging']))
>>> ('sqs', set([u'List']))
>>> ('sns', set([u'List', u'Read', u'Write', u'Permissions']))

Possible categories are Permissions, Write, Read, Tagging, and List. This data can be used to summarize statements and policies and to look for sensitive permissions.

Expanding and Minification

from policyuniverse.expander_minimizer import expand_policy
from policyuniverse.expander_minimizer import minimize_policy

policy = {
        "Statement": [{
            "Action": ["swf:res*"],
            "Resource": "*",
            "Effect": "Allow"
          }]
      }

expanded_policy = expand_policy(policy=policy)
>>> Start size: 131. End size: 286
print(expanded_policy == {
        "Statement": [{
            "Action": [
              "swf:respondactivitytaskcanceled",
              "swf:respondactivitytaskcompleted",
              "swf:respondactivitytaskfailed",
              "swf:responddecisiontaskcompleted"
            ],
            "Resource": "*",
            "Effect": "Allow"
          }]
      })
>>> True

minimized_policy = minimize_policy(policy=expanded_policy, minchars=3)
>>> Skipping prefix r because length of 1
>>> Skipping prefix re because length of 2
>>> Skipping prefix r because length of 1
>>> Skipping prefix re because length of 2
>>> Skipping prefix r because length of 1
>>> Skipping prefix re because length of 2
>>> Skipping prefix r because length of 1
>>> Skipping prefix re because length of 2
>>> Start size: 286. End size: 131

print(minimized_policy == policy)
>>> True

Metadata

Release files for policyuniverse 1.3.2.20201216

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for policyuniverse 1.3.2.20201216
File Size Uploaded
policyuniverse-1.3.2.20201216.tar.gz 300.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for policyuniverse 1.3.2.20201216
File Interpreter ABI Platform
policyuniverse-1.3.2.20201216-py2.py3-none-any.whl Python 2, Python 3 none any Details

Total release size: 608.5 kB

Release files / policyuniverse-1.3.2.20201216.tar.gz

Download URL policyuniverse-1.3.2.20201216.tar.gz
Size 300.4 kB
Tags Source
SHA-256 checksum
How to use checksums
4e6d182a1f06ea49905f5df645beff897332db4719998af8324303181da5de84
BLAKE2b-256 checksum
How to use checksums
0293541690f62a4be05fcea8b413200293478b71adcb0da3f6fe5a5c50ffe8a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.1 CPython/3.9.1

Release files / policyuniverse-1.3.2.20201216-py2.py3-none-any.whl

Download URL policyuniverse-1.3.2.20201216-py2.py3-none-any.whl
Size 308.1 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
4228b77ef9791328a687e1accd7c5e9f0bc13545b32254c588791f7af9c9a3ee
BLAKE2b-256 checksum
How to use checksums
0836ba60fe1b933cbfcde094ef2b2e6df78bce274e2f8a5100ecb423ae83c987
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.1 CPython/3.9.1

Release history Release notifications | RSS feed

This release

1.3.2.20201216 This release

2 release files

1.3.2.3

1 release file

1.3.2.2

1 release file

1.3.0.1

1 release file

1.2.0.1

1 release file

1.1.0.2

1 release file

1.0.7.4

1 release file

1.0.7.3

1 release file

1.0.7.2

1 release file

1.0.7.1

1 release file

1.0.6.3

1 release file

1.0.6.2

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page