def test_eks_gh_155(self): """test_eks_gh_155: Test EKS issue raised in GH-155""" template_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, "files", "eks-service-wide.yml", ) ) cfg = read_yaml_file(template_file_path) result = write_policy_with_template(cfg) print(json.dumps(result, indent=4)) expected_results = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "eks:ListClusters", "eks:CreateCluster" ], "Resource": [ "*" ] } ] } self.assertDictEqual(result, expected_results)
def test_gh_204_multiple_resource_types_wildcards(self): """test_gh_204_multiple_resource_types_wildcards: Address github issue #204 not having unit tests""" crud_template = { "mode": "crud", 'read': ["arn:aws:rds:us-east-1:123456789012:*:*"], 'write': ["arn:aws:rds:us-east-1:123456789012:*:*"], 'list': ["arn:aws:rds:us-east-1:123456789012:*:*"] } # Let's only check the read level ones, or that will get exhausting. expected_statement_ids = [ "RdsReadDb", "RdsReadEs", "RdsReadOg", "RdsReadPg", "RdsReadProxy", "RdsReadRi", "RdsReadSecgrp", "RdsReadSnapshot", "RdsReadSubgrp", "RdsReadTargetgroup", "MultMultNone", ] # In the real world, we would want to minimize, but that would just result in two different Sids: # RdsMult # MultMultNone # And in this test, we are trying to verify them individually to make sure they are not excluded. # So we will skip --minimize just for this test. policy = write_policy_with_template(crud_template) statement_ids = [] for statement in policy.get("Statement"): statement_ids.append(statement.get("Sid")) for expected_sid in expected_statement_ids: self.assertTrue(expected_sid in statement_ids)
def test_rds_policy_read_only(self): """test_rds_policy_read_only: Make sure that the RDS Policies work properly""" policy_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-rds-read.yml", ) ) cfg = read_yaml_file(policy_file_path) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "RdsReadDb", "Effect": "Allow", "Action": [ "rds:DownloadDBLogFilePortion", "rds:ListTagsForResource" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:db:mydatabase" ] } ] } policy = write_policy_with_template(cfg) print(json.dumps(policy, indent=4)) self.assertDictEqual(desired_output, policy)
def test_empty_strings_in_access_level_categories(self): """ test_empty_strings_in_access_level_categories: If the content of a list is an empty string, it should NOT sysexit :return: """ crud_file_input = { "mode": "crud", "name": "RoleNameWithCRUD", "read": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], "write": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], "list": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], "tagging": [""], "permissions-management": [""], } desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "SsmReadParameter", "Effect": "Allow", "Action": [ "ssm:GetParameter", "ssm:GetParameterHistory", "ssm:GetParameters", "ssm:GetParametersByPath" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test" ] }, { "Sid": "SsmWriteParameter", "Effect": "Allow", "Action": [ "ssm:DeleteParameter", "ssm:DeleteParameters", "ssm:LabelParameterVersion", "ssm:PutParameter" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test" ] }, { "Sid": "SsmListParameter", "Effect": "Allow", "Action": [ "ssm:ListTagsForResource" ], "Resource": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test" ] } ] } # with self.assertRaises(Exception): result = write_policy_with_template(crud_file_input) # print(json.dumps(result, indent=4)) self.assertDictEqual(desired_output, result)
def test_wildcard_when_not_necessary(self): """test_wildcard_when_not_necessary: Attempts bypass of CRUD mode wildcard-only""" cfg = { "mode": "crud", "name": "RoleNameWithCRUD", "permissions-management": ["arn:aws:s3:::example-org-s3-access-logs"], "wildcard-only": { "single-actions": [ # The first three are legitimately wildcard only. # Verify with `policy_sentry query action-table --service secretsmanager --wildcard-only` "ram:EnableSharingWithAwsOrganization", "ram:GetResourcePolicies", "secretsmanager:CreateSecret", # This last one can be "secret" ARN type OR wildcard. We want to prevent people from # bypassing this mechanism, while allowing them to explicitly # request specific privs that require wildcard mode. This next value - # secretsmanager:putsecretvalue - is an example of someone trying to beat the tool. "secretsmanager:PutSecretValue", ], } } db_session = connect_db("bundled") output = write_policy_with_template(db_session, cfg) # print(json.dumps(output, indent=4)) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "ram:EnableSharingWithAwsOrganization", "ram:GetResourcePolicies", "secretsmanager:CreateSecret", ], "Resource": ["*"], }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"], }, ], } self.maxDiff = None print(output) self.assertDictEqual(desired_output, output)
def write_policy_dir(input_dir, output_dir, minimize, log_level): """ write_policy, but this time with an input directory of YML/YAML files, and an output directory for all the JSON files """ set_log_level(logger, log_level) db_session = connect_db(DATABASE_FILE_PATH) input_dir = os.path.abspath(input_dir) output_dir = os.path.abspath(output_dir) if not minimize: logger.warning( "Note: --minimize option is not set. If the policy is too large, " "it can hit the AWS IAM Policy character limit. " "We'll execute as-is, but try using `--minimize 0` functionality " "for production to optimize policy size.\n") # Construct the path # Get the list of files # Write a list of the names if not check_valid_file_path(input_dir): logger.critical("Input directory is invalid") sys.exit() if not check_valid_file_path(output_dir): logger.critical("Output directory is invalid") sys.exit() input_files = glob.glob(str(input_dir + "/*.yml"), recursive=False) if not input_files: logger.critical( "Directory is empty or does not have files with *.yml extension. " "Please check the folder contents and/or extension spelling.") logger.info("Writing the policy JSON files from %s to %s...\n", input_dir, output_dir) for yaml_file in input_files: # Get the name of the file, and strip the extension. This is what the # policy name will be base_name = os.path.basename(yaml_file) base_name_no_extension = os.path.splitext( os.path.basename(yaml_file))[0] cfg = read_yaml_file(yaml_file) policy = write_policy_with_template(db_session, cfg, minimize) logger.info("Writing policy for %s\n", base_name) target_file = str(output_dir + "/" + base_name_no_extension + ".json") if os.path.exists(target_file): logger.info( "Target file for %s.json exists in the target directory. " "Removing it and writing a new file.\n", target_file, ) os.remove(target_file) write_json_file(target_file, policy) logger.info("Finished")
def test_empty_strings_in_access_level_categories(self): """ test_empty_strings_in_access_level_categories: If the content of a list is an empty string, it should NOT sysexit :return: """ crud_file_input = { "mode": "crud", "name": "RoleNameWithCRUD", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789012:role/RiskyEC2", "read": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test", ], "write": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test", ], "list": [ "arn:aws:ssm:us-east-1:123456789012:parameter/test", ], "tagging": [""], "permissions-management": [""], } desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "SsmReadParameter", "Effect": "Allow", "Action": [ "ssm:getparameter", "ssm:getparameterhistory", "ssm:getparameters", "ssm:getparametersbypath", "ssm:listtagsforresource" ], "Resource": ["arn:aws:ssm:us-east-1:123456789012:parameter/test"] }, { "Sid": "SsmWriteParameter", "Effect": "Allow", "Action": [ "ssm:deleteparameter", "ssm:deleteparameters", "ssm:labelparameterversion", "ssm:putparameter" ], "Resource": ["arn:aws:ssm:us-east-1:123456789012:parameter/test"] }] } # with self.assertRaises(Exception): result = write_policy_with_template(db_session, crud_file_input) print(json.dumps(result, indent=4)) self.assertDictEqual(desired_output, result)
def test_actions_missing_actions(self): """ test_actions_missing_actions: write-policy actions if the actions block is missing :return: """ cfg_with_missing_actions = { "mode": "actions", "name": "RoleNameWithActions", } with self.assertRaises(Exception): policy = write_policy_with_template(cfg_with_missing_actions)
def write_crud_policy(event, context): request_data = json.loads(event.get("body")) crud_template = { "mode": "crud", } if "read" in request_data: crud_template["read"] = request_data.get("read") if "write" in request_data: crud_template["write"] = request_data.get("write") if "list" in request_data: crud_template["list"] = request_data.get("list") if "tagging" in request_data: crud_template["tagging"] = request_data.get("tagging") if "permissions-management" in request_data: crud_template["permissions-management"] = request_data.get( "permissions-management") # Wildcard only section crud_template["wildcard-only"] = {} if "service-read" in request_data: crud_template["wildcard-only"]["service-read"] = request_data.get( "service-read") if "service-write" in request_data: crud_template["wildcard-only"]["service-write"] = request_data.get( "service-write") if "service-list" in request_data: crud_template["wildcard-only"]["service-list"] = request_data.get( "service-list") if "service-tagging" in request_data: crud_template["wildcard-only"]["service-tagging"] = request_data.get( "service-tagging") if "service-permissions-management" in request_data: crud_template["wildcard-only"][ "service-permissions-management"] = request_data.get( "service-permissions-management") if "single-actions" in request_data: crud_template["wildcard-only"]["single-actions"] = request_data.get( "single-actions") if "exclude-actions" in request_data: crud_template["exclude-actions"] = request_data.get("exclude-actions") if "skip-resource-constraints" in request_data: crud_template["skip-resource-constraints"] = request_data.get( "skip-resource-constraints") output = write_policy_with_template(crud_template) # logger.info(output) return output
def test_actions_missing_actions(self): """ test_actions_missing_actions: write-policy actions if the actions block is missing :return: """ cfg_with_missing_actions = { "mode": "actions", "name": "RoleNameWithActions", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789102:role/RiskyEC2", } with self.assertRaises(Exception): policy = write_policy_with_template(db_session, cfg_with_missing_actions)
def ui_response_handler(event, context): event = event['body'] print("performing decode, event value is", event) event = parse_qs(event) print(event) output_data = { 'mode': 'crud', 'name': '', 'read': [], 'write': [], 'list': [], 'tagging': [], 'permissions-management': [], 'wildcard-only': { 'single-actions': [], 'service-read': [], 'service-write': [], 'service-list': [], 'service-tagging': [], 'service-permissions-management': [] } } for key, val in event.items(): indx = key.split('_')[-1] if 'arn' in key: if key.startswith('action'): action_name = event['action_name_' + indx][0] update_data = output_data else: action_name = event['wc_name_' + indx][0] update_data = output_data['wildcard-only'] val = list(map(lambda x: x.strip(), unquote(val[0]).split(','))) update_data[action_name].extend(val) print("output data", output_data) return { "statusCode": 200, 'headers': { 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': True, 'Access-Control-Allow-Methods': 'POST,GET', 'Content-Type': 'application/json' }, "body": json.dumps(write_policy_with_template(output_data)) }
def test_allow_missing_access_level_categories_in_cfg(self): """ test_allow_missing_access_level_categories_in_cfg: write-policy when the YAML file is missing access level categories. It should write a policy regardless. """ crud_file_input = { "mode": "crud", "name": "RoleNameWithCRUD", "read": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], "write": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], "list": ["arn:aws:ssm:us-east-1:123456789012:parameter/test",], } self.maxDiff = None result = write_policy_with_template(crud_file_input) print(json.dumps(result, indent=4))
def test_allow_missing_name(self): """ test_actions_missing_name: write-policy when the YAML file is missing a name :return: """ cfg_with_missing_name = { "mode": "actions", "actions": [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ], } # This should NOT raise an exception so leaving it as-is. policy = write_policy_with_template(cfg_with_missing_name)
def test_add_wildcard_only_actions_matching_services_and_access_level( self): """test_add_wildcard_only_actions_matching_services_and_access_level: We'd never write a policy like this IRL but doing this as a quality check against how it handles the database """ policy_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-with-wildcard-service-level.yml", )) cfg = read_yaml_file(policy_file_path) output = write_policy_with_template(cfg) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "ram:EnableSharingWithAwsOrganization", "ram:GetResourcePolicies", "secretsmanager:CreateSecret", "ecr:GetAuthorizationToken", "s3:GetAccessPoint", "s3:GetAccountPublicAccessBlock", "s3:ListAccessPoints", "s3:ListJobs" ], "Resource": ["*"] }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock" ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"] }] } print(json.dumps(output, indent=4)) self.maxDiff = None self.assertDictEqual(output, desired_output)
def test_allow_missing_arn(self): """ test_actions_missing_arn: write-policy actions command when YAML file block is missing an ARN :return: """ cfg_with_missing_actions = { "mode": "actions", "name": "RoleNameWithActions", "actions": [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ], } # This should NOT raise an exception so leaving it as-is. policy = write_policy_with_template(cfg_with_missing_actions)
def test_dynamodb_arn_policy_gh_215(self): """test_dynamodb_arn_matching_gh_215: Test writing a policy with DynamoDB""" template_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, "files", "dynamodb_gh_215.yml", )) cfg = read_yaml_file(template_file_path) results = write_policy_with_template(cfg) print(json.dumps(results, indent=4)) expected_statement_ids = [ "MultMultNone", "DynamodbReadTable", "DynamodbWriteTable", ] for statement in results.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
def test_allow_missing_name(self): """ test_actions_missing_name: write-policy when the YAML file is missing a name :return: """ cfg_with_missing_name = { "mode": "actions", "description": "Why I need these privs", "role_arn": "arn:aws:iam::123456789102:role/RiskyEC2", "actions": [ "kms:CreateGrant", "kms:CreateCustomKeyStore", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", ], } # This should NOT raise an exception so leaving it as-is. policy = write_policy_with_template(db_session, cfg_with_missing_name)
def test_add_wildcard_only_actions_matching_services_and_access_level( self): """test_add_wildcard_only_actions_matching_services_and_access_level: We'd never write a policy like this IRL but doing this as a quality check against how it handles the database """ policy_file_path = abspath( join( dirname(__file__), pardir + "/" + pardir + "/examples/yml/crud-with-wildcard-service-level.yml", )) cfg = read_yaml_file(policy_file_path) output = write_policy_with_template(db_session, cfg) print(json.dumps(output, indent=4)) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "s3:GetAccessPoint", "s3:GetAccountPublicAccessBlock", "s3:ListAccessPoints" ], "Resource": ["*"] }, { "Sid": "S3PermissionsmanagementBucket", "Effect": "Allow", "Action": [ "s3:DeleteBucketPolicy", "s3:PutBucketAcl", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock" ], "Resource": ["arn:aws:s3:::example-org-s3-access-logs"] }] } self.assertDictEqual(output, desired_output)
def test_rds_policy_read_only(self): """test_rds_policy_read_only: Make sure that the RDS Policies work properly""" policy_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-rds-read.yml", )) cfg = read_yaml_file(policy_file_path) desired_output = { "Version": "2012-10-17", "Statement": [{ "Sid": "RdsReadDb", "Effect": "Allow", "Action": [ "rds:DownloadCompleteDBLogFile", "rds:DownloadDBLogFilePortion", "rds:ListTagsForResource" ], "Resource": ["arn:aws:rds:us-east-1:123456789012:db:mydatabase"] }] } expected_actions = [ "rds:DownloadCompleteDBLogFile", "rds:DownloadDBLogFilePortion", "rds:ListTagsForResource" ] output = write_policy_with_template(cfg) print(json.dumps(output, indent=4)) expected_statement_ids = ["RdsReadDb"] for statement in output.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids) for action in expected_actions: self.assertTrue(action in output["Statement"][0]["Action"])
def test_gh_204_multiple_resource_types_wildcards(self): """test_gh_204_multiple_resource_types_wildcards: Address github issue #204 not having unit tests""" crud_template = { "mode": "crud", 'read': ["arn:aws:rds:us-east-1:123456789012:*:*"], 'write': ["arn:aws:rds:us-east-1:123456789012:*:*"], 'list': ["arn:aws:rds:us-east-1:123456789012:*:*"] } expected_results_file = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, "files", "test_gh_204_multiple_resource_types_wildcards.json", )) with open(expected_results_file, "r") as yaml_file: expected_results = json.load(yaml_file) result = write_policy_with_template(crud_template) # print(json.dumps(result, indent=4)) print(len(result.get("Statement"))) self.assertTrue(len(result.get("Statement")) > 40)
def test_gh_211_write_with_empty_access_level_lists(self): crud_template = get_crud_template_dict() crud_template['read'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") crud_template['write'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") # crud_template['list'].append("arn:aws:s3:::mybucket/stuff") # by commenting out the line below, you should not get an IndexError # crud_template['permissions-management'].append("arn:aws:kms:us-east-1:123456789012:key/123456") # crud_template['tagging'].append("arn:aws:ssm:us-east-1:123456789012:parameter/test") wildcard_actions_to_add = [ "kms:createcustomkeystore", "cloudhsm:describeclusters" ] crud_template['wildcard-only']['single-actions'].extend( wildcard_actions_to_add) result = write_policy_with_template(crud_template) # print(json.dumps(result, indent=4)) expected_statement_ids = [ "MultMultNone", "SecretsmanagerReadSecret", "SecretsmanagerWriteSecret", ] for statement in result.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
def test_add_wildcard_only_actions_matching_services_and_access_level(self): """test_add_wildcard_only_actions_matching_services_and_access_level: We'd never write a policy like this IRL but doing this as a quality check against how it handles the database """ policy_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-with-wildcard-service-level.yml", ) ) cfg = read_yaml_file(policy_file_path) results = write_policy_with_template(cfg) # The Policy *should* look like this. # desired_output = { # "Version": "2012-10-17", # "Statement": [ # { # "Sid": "MultMultNone", # "Effect": "Allow", # "Action": [ # "ram:EnableSharingWithAwsOrganization", # "ram:GetResourcePolicies", # "ecr:GetAuthorizationToken", # "s3:GetAccessPoint", # "s3:GetAccountPublicAccessBlock", # "s3:ListAccessPoints", # "s3:ListJobs" # ], # "Resource": [ # "*" # ] # }, # { # "Sid": "S3PermissionsmanagementBucket", # "Effect": "Allow", # "Action": [ # "s3:DeleteBucketPolicy", # "s3:PutBucketAcl", # "s3:PutBucketPolicy", # "s3:PutBucketPublicAccessBlock" # ], # "Resource": [ # "arn:aws:s3:::example-org-s3-access-logs" # ] # } # ] # } print(json.dumps(results, indent=4)) self.maxDiff = None # To future-proof this unit test... # (1) check the Sid names sid_names = get_sid_names_from_policy(results) self.assertIn("MultMultNone", sid_names, "Sid is not in the list of expected Statement Ids") self.assertIn("S3PermissionsmanagementBucket", sid_names, "Sid is not in the list of expected Statement Ids") # (2) Check for the presence of certain actions that we know will be there statement_1 = get_statement_from_policy_using_sid(results, "MultMultNone") self.assertIn("ram:EnableSharingWithAwsOrganization", statement_1.get("Action")) self.assertIn("s3:GetAccountPublicAccessBlock", statement_1.get("Action")) statement_2 = get_statement_from_policy_using_sid(results, "S3PermissionsmanagementBucket") self.assertIn("s3:DeleteBucketPolicy", statement_2.get("Action")) self.assertIn("s3:PutBucketPolicy", statement_2.get("Action")) # (3) Check that the length of the list is at least the length that it is right now, # since we expect it to grow eventually self.assertTrue(len(statement_1.get("Action")) > 5) # Size is 6 at time of writing self.assertTrue(len(statement_2.get("Action")) > 3) # Size is 4 at time of writing
def test_rds_policy_read_write_list(self): """test_rds_policy_read_write_list: Make sure that the RDS Policies work properly for multiple levels""" policy_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, os.path.pardir, "examples", "yml", "crud-rds-mult.yml", ) ) cfg = read_yaml_file(policy_file_path) desired_output = { "Version": "2012-10-17", "Statement": [ { "Sid": "RdsReadDb", "Effect": "Allow", "Action": [ "rds:DownloadDBLogFilePortion", "rds:ListTagsForResource" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:db:mydatabase" ] }, { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": [ "*" ] }, { "Sid": "RdsWriteDb", "Effect": "Allow", "Action": [ "rds:AddRoleToDBInstance", "rds:ApplyPendingMaintenanceAction", "rds:CreateDBInstance", "rds:CreateDBInstanceReadReplica", "rds:CreateDBSnapshot", "rds:DeleteDBInstance", "rds:DeregisterDBProxyTargets", "rds:ModifyDBInstance", "rds:PromoteReadReplica", "rds:RebootDBInstance", "rds:RemoveRoleFromDBInstance", "rds:RestoreDBInstanceFromDBSnapshot", "rds:RestoreDBInstanceFromS3", "rds:RestoreDBInstanceToPointInTime", "rds:StartDBInstance", "rds:StopDBInstance" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:db:mydatabase" ] }, { "Sid": "RdsListDb", "Effect": "Allow", "Action": [ "rds:DescribeDBLogFiles", "rds:DescribeDBProxyTargets", "rds:DescribeDBSnapshots", "rds:DescribePendingMaintenanceActions", "rds:DescribeValidDBInstanceModifications" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:db:mydatabase" ] } ] } expected_statement_ids = [ "RdsReadDb", "MultMultNone", "RdsWriteDb", "RdsListDb" ] policy = write_policy_with_template(cfg) for statement in policy.get("Statement"): self.assertTrue(statement.get("Sid") in expected_statement_ids)
#!/usr/bin/env python from policy_sentry.shared.database import connect_db from policy_sentry.writing.template import get_actions_template_dict from policy_sentry.command.write_policy import write_policy_with_template import json if __name__ == '__main__': db_session = connect_db('bundled') actions_template = get_actions_template_dict() actions_to_add = [ 'kms:CreateGrant', 'kms:CreateCustomKeyStore', 'ec2:AuthorizeSecurityGroupEgress', 'ec2:AuthorizeSecurityGroupIngress' ] actions_template['actions'].extend(actions_to_add) policy = write_policy_with_template(db_session, actions_template) print(json.dumps(policy, indent=4)) """ Output: { "Version": "2012-10-17", "Statement": [ { "Sid": "KmsPermissionsmanagementKmskey", "Effect": "Allow", "Action": [ "kms:creategrant" ], "Resource": [ "arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}" ]
def test_dynamodb_arn_policy_gh_215(self): """test_dynamodb_arn_matching_gh_215: Test writing a policy with DynamoDB""" template_file_path = os.path.abspath( os.path.join( os.path.dirname(__file__), os.path.pardir, "files", "dynamodb_gh_215.yml", ) ) cfg = read_yaml_file(template_file_path) results = write_policy_with_template(cfg) print(json.dumps(results, indent=4)) expected_results = { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "dynamodb:DescribeLimits", "dynamodb:DescribeReservedCapacity", "dynamodb:DescribeReservedCapacityOfferings", "dynamodb:ListStreams", "dynamodb:ListBackups", "dynamodb:ListContributorInsights", "dynamodb:ListGlobalTables", "dynamodb:ListTables" ], "Resource": [ "*" ] }, { "Sid": "DynamodbReadTable", "Effect": "Allow", "Action": [ "dynamodb:BatchGetItem", "dynamodb:ConditionCheckItem", "dynamodb:DescribeContinuousBackups", "dynamodb:DescribeContributorInsights", "dynamodb:DescribeTable", "dynamodb:DescribeTableReplicaAutoScaling", "dynamodb:DescribeTimeToLive", "dynamodb:GetItem", "dynamodb:ListTagsOfResource", "dynamodb:Query", "dynamodb:Scan" ], "Resource": [ "arn:aws:dynamodb:us-east-1:123456789123:table/mytable" ] }, { "Sid": "DynamodbWriteTable", "Effect": "Allow", "Action": [ "dynamodb:BatchWriteItem", "dynamodb:CreateBackup", "dynamodb:CreateGlobalTable", "dynamodb:CreateTable", "dynamodb:CreateTableReplica", "dynamodb:DeleteItem", "dynamodb:DeleteTable", "dynamodb:DeleteTableReplica", "dynamodb:PutItem", "dynamodb:RestoreTableFromBackup", "dynamodb:RestoreTableToPointInTime", "dynamodb:UpdateContinuousBackups", "dynamodb:UpdateContributorInsights", "dynamodb:UpdateGlobalTable", "dynamodb:UpdateGlobalTableSettings", "dynamodb:UpdateItem", "dynamodb:UpdateTable", "dynamodb:UpdateTableReplicaAutoScaling", "dynamodb:UpdateTimeToLive" ], "Resource": [ "arn:aws:dynamodb:us-east-1:123456789123:table/mytable" ] } ] } self.assertDictEqual(results, expected_results)
async def write_iam_policy(item: WritePolicyInput): template = WritePolicyTemplate(item) output = write_policy_with_template(template.json) return output
wildcard_actions_to_add = [ "kms:createcustomkeystore", "cloudhsm:describeclusters" ] crud_template['mode'] = 'crud' crud_template['read'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") crud_template['write'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") crud_template['list'].append("arn:aws:s3:::example-org-sbx-vmimport/stuff") crud_template['permissions-management'].append( "arn:aws:kms:us-east-1:123456789012:key/123456") crud_template['wildcard'].extend(wildcard_actions_to_add) crud_template['tagging'].append( "arn:aws:ssm:us-east-1:123456789012:parameter/test") # Modify it policy = write_policy_with_template(crud_template) print(json.dumps(policy, indent=4)) """ Output: { "Version": "2012-10-17", "Statement": [ { "Sid": "MultMultNone", "Effect": "Allow", "Action": [ "kms:createcustomkeystore" ], "Resource": [ "*"
def test_gh_211_write_with_empty_access_level_lists(self): expected_results = { "Version": "2012-10-17", "Statement": [{ "Sid": "MultMultNone", "Effect": "Allow", "Action": ["cloudhsm:DescribeClusters", "kms:CreateCustomKeyStore"], "Resource": ["*"] }, { "Sid": "SecretsmanagerReadSecret", "Effect": "Allow", "Action": [ "secretsmanager:DescribeSecret", "secretsmanager:GetResourcePolicy", "secretsmanager:GetSecretValue", "secretsmanager:ListSecretVersionIds" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret" ] }, { "Sid": "SecretsmanagerWriteSecret", "Effect": "Allow", "Action": [ "secretsmanager:CancelRotateSecret", "secretsmanager:CreateSecret", "secretsmanager:DeleteSecret", "secretsmanager:PutSecretValue", "secretsmanager:RestoreSecret", "secretsmanager:RotateSecret", "secretsmanager:UpdateSecret", "secretsmanager:UpdateSecretVersionStage" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret" ] }] } crud_template = get_crud_template_dict() crud_template['read'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") crud_template['write'].append( "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret") # crud_template['list'].append("arn:aws:s3:::mybucket/stuff") # by commenting out the line below, you should not get an IndexError # crud_template['permissions-management'].append("arn:aws:kms:us-east-1:123456789012:key/123456") # crud_template['tagging'].append("arn:aws:ssm:us-east-1:123456789012:parameter/test") wildcard_actions_to_add = [ "kms:createcustomkeystore", "cloudhsm:describeclusters" ] crud_template['wildcard-only']['single-actions'].extend( wildcard_actions_to_add) result = write_policy_with_template(crud_template) # print(json.dumps(result, indent=4)) self.assertDictEqual(result, expected_results)
def test_write_policy_input_schema(self): actions_for_resources_at_access_level = { "read": [], "write": [], "list": [], "tagging": [], "permissions-management": [], } actions_for_services_without_resource_constraint_support = { "single-actions": [], "read": [], "write": [], "list": [], "tagging": [], "permissions-management": [], } wildcard_only_section = ActionsForServicesWithoutResourceConstraintSupport( read=[], write=[], list_access=[], permissions_management=[], tagging=[], single_actions=["s3:ListAllMyBuckets"]) actions_for_resources_at_access_level = ActionsForResourcesAtAccessLevel( read=["arn:aws:s3:::mybucket/*"], write=[], list_access=[], permissions_management=[], tagging=[], ) write_policy_input = WritePolicyInput( name="test", actions_for_resources_at_access_level= actions_for_resources_at_access_level, actions_for_services_without_resource_constraint_support= wildcard_only_section, skip_resource_constraints=["s3:PutObject"], exclude_actions=["kms:Delete*"]) template = WritePolicyTemplate(write_policy_input) results = write_policy_with_template(template.json) print(json.dumps(results, indent=4)) expected_results = { "Version": "2012-10-17", "Statement": [{ "Sid": "MultMultNone", "Effect": "Allow", "Action": ["s3:ListAllMyBuckets"], "Resource": ["*"] }, { "Sid": "S3ReadObject", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetObjectAcl", "s3:GetObjectLegalHold", "s3:GetObjectRetention", "s3:GetObjectTagging", "s3:GetObjectTorrent", "s3:GetObjectVersion", "s3:GetObjectVersionAcl", "s3:GetObjectVersionForReplication", "s3:GetObjectVersionTagging", "s3:GetObjectVersionTorrent" ], "Resource": ["arn:aws:s3:::mybucket/*"] }, { "Sid": "SkipResourceConstraints", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": ["*"] }] } self.assertListEqual(get_sid_names_from_policy(results), get_sid_names_from_policy(expected_results))
#!/usr/bin/env python from policy_sentry.writing.template import get_actions_template_dict from policy_sentry.command.write_policy import write_policy_with_template import json if __name__ == '__main__': actions_template = get_actions_template_dict() actions_to_add = [ 'kms:CreateGrant', 'kms:CreateCustomKeyStore', 'ec2:AuthorizeSecurityGroupEgress', 'ec2:AuthorizeSecurityGroupIngress' ] actions_template['actions'].extend(actions_to_add) policy = write_policy_with_template(actions_template) print(json.dumps(policy, indent=4)) """ Output: { "Version": "2012-10-17", "Statement": [ { "Sid": "KmsPermissionsmanagementKmskey", "Effect": "Allow", "Action": [ "kms:creategrant" ], "Resource": [ "arn:${Partition}:kms:${Region}:${Account}:key/${KeyId}" ]