Skip to content

Feature/assume role section #386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ skip-resource-constraints:
# Exclude actions from the output by specifying them here. Accepts wildcards, like kms:Delete*
exclude-actions:
- ''
# If this policy needs to include an AssumeRole action
sts:
assume-role:
- ''
assume-role-with-saml:
- ''
assume-role-with-web-identity:
- ''
```

### Step 2: Copy/paste ARNs
Expand Down
54 changes: 54 additions & 0 deletions docs/writing-policies/assume-role-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
Assume Role Actions
============

Using CRUD mode, you can add STS assume role actions to your larger CRUD policy by using the `sts` section.

**Input**:

```yaml
mode: crud
sts:
assume-role:
- arn:aws:iam::123456789012:role/demo
- arn:aws:iam::123456789012:role/demo2
assume-role-with-saml:
- arn:aws:iam::123456789012:role/demo3
assume-role-with-web-identity:
- 'arn:aws:iam::123456789012:role/demo'
```

**Output**:

```json
{
"Sid": "AssumeRole",
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": [
"arn:aws:iam::123456789012:role/demo",
"arn:aws:iam::123456789012:role/demo2"
]
},
{
"Sid": "AssumeRoleWithSAML",
"Effect": "Allow",
"Action": [
"sts:AssumeRoleWithSAML"
],
"Resource": [
"arn:aws:iam::123456789012:role/demo3"
]
},
{
"Sid": "AssumeRoleWithWebIdentity",
"Effect": "Allow",
"Action": [
"sts:AssumeRoleWithWebIdentity"
],
"Resource": [
"arn:aws:iam::123456789012:role/demo"
]
}
```
14 changes: 14 additions & 0 deletions docs/writing-policies/crud-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ wildcard-only:
- ''
service-permissions-management:
- ''
sts:
assume-role:
- ''
assume-role-with-saml:
- ''
assume-role-with-web-identity:
- ''
```

- Then just fill it out:
Expand Down Expand Up @@ -92,6 +99,13 @@ wildcard-only:
- ''
service-permissions-management:
- ''
sts:
assume-role:
- ''
assume-role-with-saml:
- ''
assume-role-with-web-identity:
- ''
```

- Run the command:
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ nav:
- "<i>Example</i>: Single actions": 'writing-policies/wildcard-only/example-single-actions.md'
- "<i>Example</i>: Service-wide": 'writing-policies/wildcard-only/example-service-wide.md'
- "<i>Example</i>: Combining approaches": 'writing-policies/wildcard-only/combined-approaches.md'
- "<i>Example</i>: AssumeRole Actions": 'writing-policies/assume-role-actions.md'
- "<b>Wildcard-only</b>":
- Introduction: 'writing-policies/wildcard-only/introduction.md'
- Single actions: 'writing-policies/wildcard-only/single-actions.md'
Expand Down
58 changes: 58 additions & 0 deletions policy_sentry/writing/sid_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,56 @@ def add_skip_resource_constraints(self, skip_resource_constraints_actions):
else:
raise Exception("Please provide 'skip_resource_constraints' as a list of IAM actions.")

def add_sts_actions(self, sts_actions):
"""
To add STS actions to the output from special YAML section
"""
if sts_actions:
# Hard coded for this special case
service_prefix = "sts"
access_level = "Write"

for action, arns in sts_actions.items():
clean_action = action.replace('-','') # Convention to follow adding dashes instead of CamelCase
service_action_data = get_action_data(service_prefix, clean_action)

# Schema validation takes care of this, but just in case. No data returned for the action
if not service_action_data:
raise Exception(f"Could not find service action data for {service_prefix} - {clean_action}")

for row in service_action_data[service_prefix]:
for arn in arns:
if not arn: # skip the - '' situation
continue
if (
does_arn_match(arn, row["resource_arn_format"])
and row["access_level"] == access_level
):
raw_arn_format = row["resource_arn_format"]

# Each action will get its own namespace sts:AssumeRole -> AssumeRole
# -1 index is a neat trick if the colon ever goes away we won't get an index error.
sid_namespace = row["action"].split(':')[-1]

temp_sid_dict = {
"arn": [arn],
"service": service_prefix,
"access_level": access_level,
"arn_format": raw_arn_format,
"actions": [row["action"]],
"conditions": [], # TODO: Add conditions
}

# Using a custom namespace and not gathering actions so no need to find
# dependent actions either, though we could do it here

if sid_namespace in self.sids.keys():
# If the ARN already exists there, skip it.
if arn not in self.sids[sid_namespace]["arn"]:
self.sids[sid_namespace]["arn"].append(arn)
else:
self.sids[sid_namespace] = temp_sid_dict

def add_requested_service_wide(self, service_prefixes, access_level):
"""
When a user requests all wildcard-only actions available under a service at a specific access level
Expand Down Expand Up @@ -486,6 +536,14 @@ def process_template(self, cfg, minimize=None):
self.add_action_without_resource_constraint(
skip_resource_constraints_action, "SkipResourceConstraints"
)

# STS Section
if cfg.get("sts"):
logger.debug(
f"STS section detected. Building assume role policy statement"
)
self.add_sts_actions(cfg['sts'])

elif cfg.get("mode") == "actions":
check_actions_schema(cfg)
if "actions" in cfg.keys():
Expand Down
15 changes: 14 additions & 1 deletion policy_sentry/writing/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@
# Exclude actions from the output by specifying them here. Accepts wildcards, like kms:Delete*
exclude-actions:
- ''
# If this policy needs to include an AssumeRole action
sts:
assume-role:
- ''
assume-role-with-saml:
- ''
assume-role-with-web-identity:
- ''
"""

CRUD_TEMPLATE_DICT = {
Expand All @@ -61,7 +69,12 @@
"service-permissions-management": [],
},
"skip-resource-constraints": [],
"exclude-actions": []
"exclude-actions": [],
"sts": {
"assume-role": [],
"assume-role-with-saml": [],
"assume-role-with-web-identity": []
}
}

ACTIONS_TEMPLATE_DICT = {"mode": "actions", "name": "", "actions": []}
Expand Down
4 changes: 3 additions & 1 deletion policy_sentry/writing/validate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""
Validation for the Policy Sentry YML Templates.
"""
from ast import Or
import logging
from schema import Optional, Schema, And, Use, SchemaError
from schema import Optional, Schema, And, Use, Regex, SchemaError

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -51,6 +52,7 @@ def check(conf_schema, conf):
},
Optional("skip-resource-constraints"): [str],
Optional("exclude-actions"): [str],
Optional("sts"): dict({And(Use(str), Regex(r'^assume-role(-with-)*(saml|web-identity)*$')): [str]}),
}
)

Expand Down
31 changes: 31 additions & 0 deletions test/writing/test_sid_group_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,3 +608,34 @@ def test_exclude_actions_empty_sid_from_crud_output(self):
]
}
self.assertDictEqual(result, expected_result)


def test_write_template_with_sts_actions(self):
cfg = {
"mode": "crud",
"name": "RoleNameWithCRUD",
"sts": {"assume-role-with-web-identity": ["arn:aws:iam::123456789012:role/demo"]},
"list": [
"arn:aws:secretsmanager:us-east-1:123456789012:secret:anothersecret"
],
}
sid_group = SidGroup()
rendered_policy = sid_group.process_template(cfg)
desired_output = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRoleWithWebIdentity",
"Effect": "Allow",
"Action": [
"sts:AssumeRoleWithWebIdentity"
],
"Resource": [
"arn:aws:iam::123456789012:role/demo"
]
}
]
}

# print(json.dumps(rendered_policy, indent=4))
self.assertEqual(rendered_policy, desired_output)
8 changes: 8 additions & 0 deletions test/writing/test_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ def test_crud_template(self):
# Exclude actions from the output by specifying them here. Accepts wildcards, like kms:Delete*
exclude-actions:
- ''
# If this policy needs to include an AssumeRole action
sts:
assume-role:
- ''
assume-role-with-saml:
- ''
assume-role-with-web-identity:
- ''
"""
crud_template = create_crud_template()
self.maxDiff = None
Expand Down
15 changes: 15 additions & 0 deletions test/writing/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@
"wrist": ["arn:aws:s3:::example-org-sbx-vmimport",],
}

valid_crud_with_sts_actions = {
"mode": "crud",
"name": "RoleNameWithCRUD",
"sts": {"assume-role": ["arn:aws:iam::123456789012:role/demo",]}
}

invalid_crud_with_invalid_sts_action = {
"mode": "crud",
"name": "RoleNameWithCRUD",
"sts": {"assume-role-fake-something": ["arn:aws:iam::123456789012:role/demo",]}
}


class YMLSchemaTestCase(unittest.TestCase):
def test_actions_schema(self):
Expand All @@ -54,5 +66,8 @@ def test_crud_schema(self):
result = check_crud_schema(valid_cfg_for_crud)
self.assertTrue(result)
self.assertTrue(check_crud_schema(valid_crud_with_one_item_only))
self.assertTrue(check_crud_schema(valid_crud_with_sts_actions))
with self.assertRaises(Exception):
check_crud_schema(invalid_crud_with_mispelled_category)
with self.assertRaises(Exception):
check_crud_schema(invalid_crud_with_invalid_sts_action)
14 changes: 13 additions & 1 deletion test/writing/test_write_policy_library_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@
"Resource": [
"arn:aws:kms:us-east-1:123456789012:key/123456"
]
},
{
"Sid": "AssumeRole",
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": [
"arn:aws:iam::123456789012:role/demo"
]
}
]
}
Expand Down Expand Up @@ -181,6 +191,7 @@ def test_write_crud_policy_with_library_only(self):
"arn:aws:ssm:us-east-1:123456789012:parameter/test"
)
another_crud_template["wildcard-only"]["single-actions"].extend(wildcard_actions_to_add)
another_crud_template["sts"]["assume-role"].append("arn:aws:iam::123456789012:role/demo")
# Modify it
sid_group = SidGroup()
# minimize = None
Expand All @@ -193,7 +204,8 @@ def test_write_crud_policy_with_library_only(self):
"SecretsmanagerWriteSecret",
"S3ListObject",
"SsmTaggingParameter",
"KmsPermissionsmanagementKey"
"KmsPermissionsmanagementKey",
"AssumeRole"
]
self.maxDiff = None
print(json.dumps(result, indent=4))
Expand Down