policyuniverse


Namepolicyuniverse JSON
Version 1.5.1.20231109 PyPI version JSON
download
home_pagehttps://github.com/Netflix-Skunkworks/policyuniverse
SummaryParse and Process AWS IAM Policies, Statements, ARNs, and wildcards.
upload_time2023-11-30 19:12:46
maintainer
docs_urlNone
authorPatrick Kelley
requires_python>=3.7
license
keywords iam arn action_groups condition policy statement wildcard
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # PolicyUniverse

[![Version](http://img.shields.io/pypi/v/policyuniverse.svg?style=flat)](https://pypi.python.org/pypi/policyuniverse/)

[![Build Status](https://github.com/Netflix-Skunkworks/policyuniverse/workflows/Python%20package/badge.svg)](https://github.com/Netflix-Skunkworks/policyuniverse/actions)

[![Updater Status](https://github.com/Netflix-Skunkworks/policyuniverse/actions/workflows/updater.yml/badge.svg)](https://github.com/Netflix-Skunkworks/policyuniverse/actions/workflows/updater.yml)

[![Coverage Status](https://coveralls.io/repos/github/Netflix-Skunkworks/policyuniverse/badge.svg?branch=master&1)](https://coveralls.io/github/Netflix-Skunkworks/policyuniverse?branch=master)

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/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](policyuniverse/data.json).

_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:

- [ARN class](#reading-arns)
- [Policy class](#iam-and-resource-policies)
- [Statement class](#statements)
- [Action Categories](#action-categories)
- [Expanding and Minification](#expanding-and-minification)

## Reading ARNs

```python
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
```python
# 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:

```python
# 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.

```python
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
```python
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
```python
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
```


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Netflix-Skunkworks/policyuniverse",
    "name": "policyuniverse",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "iam,arn,action_groups,condition,policy,statement,wildcard",
    "author": "Patrick Kelley",
    "author_email": "patrickbarrettkelley@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/03/a2/6cf14186b746fbcab73e507968e0b1927ad2e91dcb67af967f65d6cbe6c1/policyuniverse-1.5.1.20231109.tar.gz",
    "platform": null,
    "description": "# PolicyUniverse\n\n[![Version](http://img.shields.io/pypi/v/policyuniverse.svg?style=flat)](https://pypi.python.org/pypi/policyuniverse/)\n\n[![Build Status](https://github.com/Netflix-Skunkworks/policyuniverse/workflows/Python%20package/badge.svg)](https://github.com/Netflix-Skunkworks/policyuniverse/actions)\n\n[![Updater Status](https://github.com/Netflix-Skunkworks/policyuniverse/actions/workflows/updater.yml/badge.svg)](https://github.com/Netflix-Skunkworks/policyuniverse/actions/workflows/updater.yml)\n\n[![Coverage Status](https://coveralls.io/repos/github/Netflix-Skunkworks/policyuniverse/badge.svg?branch=master&1)](https://coveralls.io/github/Netflix-Skunkworks/policyuniverse?branch=master)\n\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)\n\nThis package provides classes to parse AWS IAM and Resource Policies.\n\nAdditionally, this package can expand wildcards in AWS Policies using permissions obtained from the AWS Policy Generator.\n\nSee the [Service and Permissions data](policyuniverse/data.json).\n\n_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._ \ud83d\udca9\n\n# Install:\n\n`pip install policyuniverse`\n\n# Usage:\n\n- [ARN class](#reading-arns)\n- [Policy class](#iam-and-resource-policies)\n- [Statement class](#statements)\n- [Action Categories](#action-categories)\n- [Expanding and Minification](#expanding-and-minification)\n\n## Reading ARNs\n\n```python\nfrom policyuniverse.arn import ARN\narn = ARN('arn:aws:iam::012345678910:role/SomeTestRoleForTesting')\nassert arn.error == False\nassert arn.tech == 'iam'\nassert arn.region == ''  # IAM is universal/global\nassert arn.account_number == '012345678910'\nassert arn.name == 'role/SomeTestRoleForTesting'\nassert arn.partition == 'aws'\nassert arn.root == False  # Not the root ARN\nassert arn.service == False  # Not an AWS service like lambda.amazonaws.com\n\narn = ARN('012345678910')\nassert arn.account_number == '012345678910'\n\narn = ARN('lambda.amazonaws.com')\nassert arn.service == True\nassert arn.tech == 'lambda'\n```\n\n## IAM and Resource Policies\n\n### Policy with multiple statements\n```python\n# Two statements, both with conditions\npolicy05 = dict(\n    Version='2010-08-14',\n    Statement=[\n        dict(\n            Effect='Allow',\n            Principal='arn:aws:iam::012345678910:root',\n            Action=['s3:*'],\n            Resource='*',\n            Condition={\n                'IpAddress': {\n                    'AWS:SourceIP': ['0.0.0.0/0']\n                }}),\n        dict(\n            Effect='Allow',\n            Principal='arn:aws:iam::*:role/Hello',\n            Action=['ec2:*'],\n            Resource='*',\n            Condition={\n                'StringLike': {\n                    'AWS:SourceOwner': '012345678910'\n                }})\n        ])\n\nfrom policyuniverse.policy import Policy\nfrom policyuniverse.statement import ConditionTuple, PrincipalTuple\n\npolicy = Policy(policy05)\nassert policy.whos_allowed() == set([\n    PrincipalTuple(category='principal', value='arn:aws:iam::*:role/Hello'),\n    PrincipalTuple(category='principal', value='arn:aws:iam::012345678910:root'),\n    ConditionTuple(category='cidr', value='0.0.0.0/0'),\n    ConditionTuple(category='account', value='012345678910')\n])\n\n# The given policy is not internet accessible.\n# The first statement is limited by the principal, and the condition is basically a no-op.\n# The second statement has a wildcard principal, but uses the condition to lock it down.\nassert policy.is_internet_accessible() == False\n```\n\n### Internet Accessible Policy:\n\n```python\n# An internet accessible policy:\npolicy01 = dict(\n    Version='2012-10-08',\n    Statement=dict(\n        Effect='Allow',\n        Principal='*',\n        Action=['rds:*'],\n        Resource='*',\n        Condition={\n            'IpAddress': {\n                'AWS:SourceIP': ['0.0.0.0/0']\n            }\n        }))\n\npolicy = Policy(policy01)\nassert policy.is_internet_accessible() == True\nassert policy.internet_accessible_actions() == set(['rds:*'])\n```\n\n## Statements\n\nA policy is simply a collection of statements.\n\n```python\nstatement12 = dict(\n    Effect='Allow',\n    Principal='*',\n    Action=['rds:*'],\n    Resource='*',\n    Condition={\n        'StringEquals': {\n            'AWS:SourceVPC': 'vpc-111111',\n            'AWS:Sourcevpce': 'vpce-111111',\n            'AWS:SourceOwner': '012345678910',\n            'AWS:SourceAccount': '012345678910'\n        },\n        'StringLike': {\n            'AWS:userid': 'AROAI1111111111111111:*'\n        },\n        'ARNLike': {\n            'AWS:SourceArn': 'arn:aws:iam::012345678910:role/Admin'\n        },\n        'IpAddressIfExists': {\n            'AWS:SourceIP': [\n                '123.45.67.89',\n                '10.0.7.0/24',\n                '172.16.0.0/16']\n        }\n    })\n\nfrom policyuniverse.statement import Statement\nfrom policyuniverse.statement import ConditionTuple, PrincipalTuple\n\nstatement = Statement(statement12)\nassert statement.effect == 'Allow'\nassert statement.actions == set(['rds:*'])\n\n# rds:* expands out to ~88 individual permissions\nassert len(statement.actions_expanded) == 88\n\nassert statement.uses_not_principal() == False\nassert statement.principals == set(['*'])\nassert statement.condition_arns == set(['arn:aws:iam::012345678910:role/Admin'])\nassert statement.condition_accounts == set(['012345678910'])\nassert statement.condition_userids == set(['AROAI1111111111111111:*'])\nassert statement.condition_cidrs == set(['10.0.7.0/24', '172.16.0.0/16', '123.45.67.89'])\nassert statement.condition_vpcs == set(['vpc-111111'])\nassert statement.condition_vpces == set(['vpce-111111'])\nassert statement.is_internet_accessible() == False\nassert statement.whos_allowed() == set([\n    PrincipalTuple(category='principal', value='*'),\n    ConditionTuple(category='cidr', value='123.45.67.89'),\n    ConditionTuple(category='account', value='012345678910'),\n    ConditionTuple(category='userid', value='AROAI1111111111111111:*'),\n    ConditionTuple(category='vpc', value='vpc-111111'),\n    ConditionTuple(category='arn', value='arn:aws:iam::012345678910:role/Admin'),\n    ConditionTuple(category='cidr', value='172.16.0.0/16'),\n    ConditionTuple(category='vpce', value='vpce-111111'),\n    ConditionTuple(category='cidr', value='10.0.7.0/24')])\n\n```\n\n\n## Action Categories\n```python\npolicy = {\n        \"Statement\": [{\n            \"Action\": [\"s3:put*\", \"sqs:get*\", \"sns:*\"],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n          }]\n      }\n\nfrom policyuniverse.policy import Policy\np = Policy(policy)\nfor k, v in p.action_summary().items():\n    print(k,v)\n>>> ('s3', set([u'Write', u'Permissions', u'Tagging']))\n>>> ('sqs', set([u'List']))\n>>> ('sns', set([u'List', u'Read', u'Write', u'Permissions']))\n```\nPossible categories are `Permissions`, `Write`, `Read`, `Tagging`, and `List`.  This data can be used to summarize statements and policies and to look for sensitive permissions.\n\n## Expanding and Minification\n```python\nfrom policyuniverse.expander_minimizer import expand_policy\nfrom policyuniverse.expander_minimizer import minimize_policy\n\npolicy = {\n        \"Statement\": [{\n            \"Action\": [\"swf:res*\"],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n          }]\n      }\n\nexpanded_policy = expand_policy(policy=policy)\n>>> Start size: 131. End size: 286\nprint(expanded_policy == {\n        \"Statement\": [{\n            \"Action\": [\n              \"swf:respondactivitytaskcanceled\",\n              \"swf:respondactivitytaskcompleted\",\n              \"swf:respondactivitytaskfailed\",\n              \"swf:responddecisiontaskcompleted\"\n            ],\n            \"Resource\": \"*\",\n            \"Effect\": \"Allow\"\n          }]\n      })\n>>> True\n\nminimized_policy = minimize_policy(policy=expanded_policy, minchars=3)\n>>> Skipping prefix r because length of 1\n>>> Skipping prefix re because length of 2\n>>> Skipping prefix r because length of 1\n>>> Skipping prefix re because length of 2\n>>> Skipping prefix r because length of 1\n>>> Skipping prefix re because length of 2\n>>> Skipping prefix r because length of 1\n>>> Skipping prefix re because length of 2\n>>> Start size: 286. End size: 131\n\nprint(minimized_policy == policy)\n>>> True\n```\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Parse and Process AWS IAM Policies, Statements, ARNs, and wildcards.",
    "version": "1.5.1.20231109",
    "project_urls": {
        "Homepage": "https://github.com/Netflix-Skunkworks/policyuniverse"
    },
    "split_keywords": [
        "iam",
        "arn",
        "action_groups",
        "condition",
        "policy",
        "statement",
        "wildcard"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "41f565b66420c275e9b26513fdd6d84687403d11ac8be4650b67d1e5572b8f48",
                "md5": "6d5f2ed284a1fe71b11cfffefed7b330",
                "sha256": "0b0ece0ee8285af31fc39ce09c82a551ca62e62bc2842e23952503bccb973321"
            },
            "downloads": -1,
            "filename": "policyuniverse-1.5.1.20231109-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6d5f2ed284a1fe71b11cfffefed7b330",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.7",
            "size": 484251,
            "upload_time": "2023-11-30T19:12:43",
            "upload_time_iso_8601": "2023-11-30T19:12:43.463346Z",
            "url": "https://files.pythonhosted.org/packages/41/f5/65b66420c275e9b26513fdd6d84687403d11ac8be4650b67d1e5572b8f48/policyuniverse-1.5.1.20231109-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "03a26cf14186b746fbcab73e507968e0b1927ad2e91dcb67af967f65d6cbe6c1",
                "md5": "b0b3c4b45e9d90190c42dd5b02cbe5d0",
                "sha256": "74e56d410560915c2c5132e361b0130e4bffe312a2f45230eac50d7c094bc40a"
            },
            "downloads": -1,
            "filename": "policyuniverse-1.5.1.20231109.tar.gz",
            "has_sig": false,
            "md5_digest": "b0b3c4b45e9d90190c42dd5b02cbe5d0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 469645,
            "upload_time": "2023-11-30T19:12:46",
            "upload_time_iso_8601": "2023-11-30T19:12:46.297481Z",
            "url": "https://files.pythonhosted.org/packages/03/a2/6cf14186b746fbcab73e507968e0b1927ad2e91dcb67af967f65d6cbe6c1/policyuniverse-1.5.1.20231109.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-11-30 19:12:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Netflix-Skunkworks",
    "github_project": "policyuniverse",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "policyuniverse"
}
        
Elapsed time: 0.76246s