cartography 0.104.0rc2__py3-none-any.whl → 0.123.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cartography/_version.py +16 -3
- cartography/cli.py +466 -5
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +357 -8
- cartography/config.py +153 -0
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +0 -186
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
- cartography/driftdetect/cli.py +3 -2
- cartography/graph/cleanupbuilder.py +198 -41
- cartography/graph/job.py +54 -6
- cartography/graph/querybuilder.py +528 -27
- cartography/graph/statement.py +5 -1
- cartography/intel/airbyte/__init__.py +105 -0
- cartography/intel/airbyte/connections.py +120 -0
- cartography/intel/airbyte/destinations.py +81 -0
- cartography/intel/airbyte/organizations.py +59 -0
- cartography/intel/airbyte/sources.py +78 -0
- cartography/intel/airbyte/tags.py +64 -0
- cartography/intel/airbyte/users.py +106 -0
- cartography/intel/airbyte/util.py +122 -0
- cartography/intel/airbyte/workspaces.py +63 -0
- cartography/intel/aws/__init__.py +24 -9
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +253 -22
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +17 -39
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +150 -4
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +7 -3
- cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
- cartography/intel/aws/ec2/instances.py +25 -1
- cartography/intel/aws/ec2/internet_gateways.py +4 -2
- cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
- cartography/intel/aws/ec2/network_interfaces.py +5 -1
- cartography/intel/aws/ec2/reserved_instances.py +3 -1
- cartography/intel/aws/ec2/security_groups.py +140 -122
- cartography/intel/aws/ec2/snapshots.py +47 -84
- cartography/intel/aws/ec2/subnets.py +37 -63
- cartography/intel/aws/ec2/tgw.py +11 -5
- cartography/intel/aws/ec2/volumes.py +1 -1
- cartography/intel/aws/ec2/vpc.py +140 -124
- cartography/intel/aws/ec2/vpc_peerings.py +262 -125
- cartography/intel/aws/ecr.py +269 -98
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +251 -380
- cartography/intel/aws/efs.py +179 -11
- cartography/intel/aws/elasticache.py +102 -79
- cartography/intel/aws/elasticsearch.py +13 -4
- cartography/intel/aws/eventbridge.py +164 -0
- cartography/intel/aws/glue.py +181 -0
- cartography/intel/aws/guardduty.py +443 -0
- cartography/intel/aws/iam.py +750 -493
- cartography/intel/aws/identitycenter.py +605 -83
- cartography/intel/aws/inspector.py +221 -105
- cartography/intel/aws/kms.py +173 -201
- cartography/intel/aws/lambda_function.py +272 -189
- cartography/intel/aws/organizations.py +10 -9
- cartography/intel/aws/permission_relationships.py +10 -20
- cartography/intel/aws/rds.py +337 -446
- cartography/intel/aws/redshift.py +9 -4
- cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
- cartography/intel/aws/resources.py +18 -0
- cartography/intel/aws/route53.py +386 -332
- cartography/intel/aws/s3.py +322 -14
- cartography/intel/aws/secretsmanager.py +81 -49
- cartography/intel/aws/securityhub.py +3 -1
- cartography/intel/aws/sns.py +62 -2
- cartography/intel/aws/sqs.py +36 -90
- cartography/intel/aws/ssm.py +3 -5
- cartography/intel/azure/__init__.py +202 -48
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +59 -112
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +222 -361
- cartography/intel/azure/data_factory.py +85 -0
- cartography/intel/azure/data_factory_dataset.py +128 -0
- cartography/intel/azure/data_factory_linked_service.py +119 -0
- cartography/intel/azure/data_factory_pipeline.py +142 -0
- cartography/intel/azure/data_lake.py +124 -0
- cartography/intel/azure/event_grid.py +94 -0
- cartography/intel/azure/functions.py +124 -0
- cartography/intel/azure/load_balancers.py +263 -0
- cartography/intel/azure/logic_apps.py +101 -0
- cartography/intel/azure/monitor.py +105 -0
- cartography/intel/azure/network.py +467 -0
- cartography/intel/azure/permission_relationships.py +466 -0
- cartography/intel/azure/rbac.py +309 -0
- cartography/intel/azure/resource_groups.py +82 -0
- cartography/intel/azure/security_center.py +106 -0
- cartography/intel/azure/sql.py +145 -292
- cartography/intel/azure/storage.py +185 -262
- cartography/intel/azure/subscription.py +21 -43
- cartography/intel/azure/tenant.py +39 -30
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +49 -174
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/create_indexes.py +2 -1
- cartography/intel/crowdstrike/spotlight.py +5 -2
- cartography/intel/dns.py +5 -2
- cartography/intel/entra/__init__.py +100 -1
- cartography/intel/entra/app_role_assignments.py +284 -0
- cartography/intel/entra/applications.py +182 -0
- cartography/intel/entra/federation/__init__.py +0 -0
- cartography/intel/entra/federation/aws_identity_center.py +77 -0
- cartography/intel/entra/groups.py +198 -0
- cartography/intel/entra/ou.py +48 -24
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +105 -57
- cartography/intel/gcp/__init__.py +334 -396
- cartography/intel/gcp/bigtable_app_profile.py +101 -0
- cartography/intel/gcp/bigtable_backup.py +91 -0
- cartography/intel/gcp/bigtable_cluster.py +93 -0
- cartography/intel/gcp/bigtable_instance.py +86 -0
- cartography/intel/gcp/bigtable_table.py +87 -0
- cartography/intel/gcp/cai.py +292 -0
- cartography/intel/gcp/clients.py +112 -0
- cartography/intel/gcp/compute.py +128 -119
- cartography/intel/gcp/crm/__init__.py +0 -0
- cartography/intel/gcp/crm/folders.py +114 -0
- cartography/intel/gcp/crm/orgs.py +70 -0
- cartography/intel/gcp/crm/projects.py +120 -0
- cartography/intel/gcp/dns.py +83 -169
- cartography/intel/gcp/gke.py +72 -113
- cartography/intel/gcp/iam.py +111 -91
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +75 -159
- cartography/intel/github/__init__.py +62 -25
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +463 -85
- cartography/intel/github/teams.py +3 -3
- cartography/intel/github/users.py +5 -0
- cartography/intel/github/util.py +12 -0
- cartography/intel/googleworkspace/__init__.py +193 -0
- cartography/intel/googleworkspace/devices.py +254 -0
- cartography/intel/googleworkspace/groups.py +568 -0
- cartography/intel/googleworkspace/oauth_apps.py +259 -0
- cartography/intel/googleworkspace/tenant.py +85 -0
- cartography/intel/googleworkspace/users.py +138 -0
- cartography/intel/gsuite/__init__.py +17 -9
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/computers.py +7 -1
- cartography/intel/keycloak/__init__.py +153 -0
- cartography/intel/keycloak/authenticationexecutions.py +322 -0
- cartography/intel/keycloak/authenticationflows.py +77 -0
- cartography/intel/keycloak/clients.py +187 -0
- cartography/intel/keycloak/groups.py +126 -0
- cartography/intel/keycloak/identityproviders.py +94 -0
- cartography/intel/keycloak/organizations.py +163 -0
- cartography/intel/keycloak/realms.py +61 -0
- cartography/intel/keycloak/roles.py +202 -0
- cartography/intel/keycloak/scopes.py +73 -0
- cartography/intel/keycloak/users.py +70 -0
- cartography/intel/keycloak/util.py +47 -0
- cartography/intel/kubernetes/__init__.py +60 -14
- cartography/intel/kubernetes/clusters.py +86 -0
- cartography/intel/kubernetes/eks.py +402 -0
- cartography/intel/kubernetes/namespaces.py +59 -57
- cartography/intel/kubernetes/pods.py +168 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -67
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/oci/iam.py +23 -9
- cartography/intel/oci/organizations.py +3 -1
- cartography/intel/oci/utils.py +28 -5
- cartography/intel/okta/applications.py +15 -5
- cartography/intel/okta/awssaml.py +14 -10
- cartography/intel/okta/factors.py +3 -1
- cartography/intel/okta/groups.py +5 -2
- cartography/intel/okta/organization.py +3 -1
- cartography/intel/okta/origins.py +3 -1
- cartography/intel/okta/roles.py +5 -2
- cartography/intel/okta/users.py +10 -2
- cartography/intel/ontology/__init__.py +44 -0
- cartography/intel/ontology/devices.py +54 -0
- cartography/intel/ontology/users.py +54 -0
- cartography/intel/ontology/utils.py +176 -0
- cartography/intel/pagerduty/escalation_policies.py +13 -6
- cartography/intel/pagerduty/schedules.py +9 -4
- cartography/intel/pagerduty/services.py +7 -3
- cartography/intel/pagerduty/teams.py +5 -2
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/scaleway/__init__.py +127 -0
- cartography/intel/scaleway/iam/__init__.py +0 -0
- cartography/intel/scaleway/iam/apikeys.py +71 -0
- cartography/intel/scaleway/iam/applications.py +71 -0
- cartography/intel/scaleway/iam/groups.py +71 -0
- cartography/intel/scaleway/iam/users.py +71 -0
- cartography/intel/scaleway/instances/__init__.py +0 -0
- cartography/intel/scaleway/instances/flexibleips.py +86 -0
- cartography/intel/scaleway/instances/instances.py +92 -0
- cartography/intel/scaleway/projects.py +79 -0
- cartography/intel/scaleway/storage/__init__.py +0 -0
- cartography/intel/scaleway/storage/snapshots.py +86 -0
- cartography/intel/scaleway/storage/volumes.py +84 -0
- cartography/intel/scaleway/utils.py +37 -0
- cartography/intel/sentinelone/__init__.py +75 -0
- cartography/intel/sentinelone/account.py +140 -0
- cartography/intel/sentinelone/agent.py +139 -0
- cartography/intel/sentinelone/api.py +124 -0
- cartography/intel/sentinelone/application.py +248 -0
- cartography/intel/sentinelone/cve.py +119 -0
- cartography/intel/sentinelone/utils.py +28 -0
- cartography/intel/slack/__init__.py +78 -0
- cartography/intel/slack/channels.py +80 -0
- cartography/intel/slack/groups.py +90 -0
- cartography/intel/slack/teams.py +65 -0
- cartography/intel/slack/users.py +57 -0
- cartography/intel/slack/utils.py +29 -0
- cartography/intel/spacelift/__init__.py +161 -0
- cartography/intel/spacelift/account.py +73 -0
- cartography/intel/spacelift/ec2_ownership.py +280 -0
- cartography/intel/spacelift/runs.py +463 -0
- cartography/intel/spacelift/spaces.py +112 -0
- cartography/intel/spacelift/stacks.py +119 -0
- cartography/intel/spacelift/util.py +122 -0
- cartography/intel/spacelift/workerpools.py +131 -0
- cartography/intel/spacelift/workers.py +128 -0
- cartography/intel/trivy/__init__.py +272 -0
- cartography/intel/trivy/scanner.py +386 -0
- cartography/models/airbyte/__init__.py +0 -0
- cartography/models/airbyte/connection.py +138 -0
- cartography/models/airbyte/destination.py +75 -0
- cartography/models/airbyte/organization.py +19 -0
- cartography/models/airbyte/source.py +75 -0
- cartography/models/airbyte/stream.py +74 -0
- cartography/models/airbyte/tag.py +69 -0
- cartography/models/airbyte/user.py +115 -0
- cartography/models/airbyte/workspace.py +46 -0
- cartography/models/anthropic/apikey.py +4 -0
- cartography/models/anthropic/user.py +4 -0
- cartography/models/aws/acm/__init__.py +0 -0
- cartography/models/aws/acm/certificate.py +75 -0
- cartography/models/aws/apigateway/__init__.py +0 -0
- cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
- cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
- cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +45 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
- cartography/models/aws/codebuild/__init__.py +0 -0
- cartography/models/aws/codebuild/project.py +49 -0
- cartography/models/aws/cognito/__init__.py +0 -0
- cartography/models/aws/cognito/identity_pool.py +70 -0
- cartography/models/aws/cognito/user_pool.py +47 -0
- cartography/models/aws/dynamodb/tables.py +2 -0
- cartography/models/aws/ec2/instances.py +25 -1
- cartography/models/aws/ec2/networkinterfaces.py +4 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +2 -0
- cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +20 -0
- cartography/models/aws/ec2/vpc.py +46 -0
- cartography/models/aws/ec2/vpc_cidr.py +102 -0
- cartography/models/aws/ec2/vpc_peering.py +157 -0
- cartography/models/aws/ecr/__init__.py +0 -0
- cartography/models/aws/ecr/image.py +146 -0
- cartography/models/aws/ecr/image_layer.py +107 -0
- cartography/models/aws/ecr/repository.py +72 -0
- cartography/models/aws/ecr/repository_image.py +95 -0
- cartography/models/aws/ecs/__init__.py +0 -0
- cartography/models/aws/ecs/clusters.py +64 -0
- cartography/models/aws/ecs/container_definitions.py +93 -0
- cartography/models/aws/ecs/container_instances.py +84 -0
- cartography/models/aws/ecs/containers.py +101 -0
- cartography/models/aws/ecs/services.py +134 -0
- cartography/models/aws/ecs/task_definitions.py +135 -0
- cartography/models/aws/ecs/tasks.py +134 -0
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/aws/efs/file_system.py +60 -0
- cartography/models/aws/efs/mount_target.py +29 -2
- cartography/models/aws/elasticache/__init__.py +0 -0
- cartography/models/aws/elasticache/cluster.py +65 -0
- cartography/models/aws/elasticache/topic.py +67 -0
- cartography/models/aws/eventbridge/__init__.py +0 -0
- cartography/models/aws/eventbridge/rule.py +77 -0
- cartography/models/aws/eventbridge/target.py +71 -0
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/guardduty/__init__.py +1 -0
- cartography/models/aws/guardduty/detectors.py +50 -0
- cartography/models/aws/guardduty/findings.py +121 -0
- cartography/models/aws/iam/access_key.py +103 -0
- cartography/models/aws/iam/account_role.py +24 -0
- cartography/models/aws/iam/federated_principal.py +60 -0
- cartography/models/aws/iam/group.py +60 -0
- cartography/models/aws/iam/group_membership.py +27 -0
- cartography/models/aws/iam/inline_policy.py +78 -0
- cartography/models/aws/iam/managed_policy.py +51 -0
- cartography/models/aws/iam/policy_statement.py +57 -0
- cartography/models/aws/iam/role.py +83 -0
- cartography/models/aws/iam/root_principal.py +52 -0
- cartography/models/aws/iam/service_principal.py +30 -0
- cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
- cartography/models/aws/iam/user.py +59 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
- cartography/models/aws/identitycenter/awspermissionset.py +70 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +49 -9
- cartography/models/aws/inspector/findings.py +37 -0
- cartography/models/aws/inspector/packages.py +1 -31
- cartography/models/aws/kms/__init__.py +0 -0
- cartography/models/aws/kms/aliases.py +86 -0
- cartography/models/aws/kms/grants.py +65 -0
- cartography/models/aws/kms/keys.py +88 -0
- cartography/models/aws/lambda_function/__init__.py +0 -0
- cartography/models/aws/lambda_function/alias.py +74 -0
- cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
- cartography/models/aws/lambda_function/lambda_function.py +91 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +91 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/rds/instance.py +156 -0
- cartography/models/aws/rds/snapshot.py +108 -0
- cartography/models/aws/rds/subnet_group.py +101 -0
- cartography/models/aws/route53/__init__.py +0 -0
- cartography/models/aws/route53/dnsrecord.py +235 -0
- cartography/models/aws/route53/nameserver.py +63 -0
- cartography/models/aws/route53/subzone.py +40 -0
- cartography/models/aws/route53/zone.py +47 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +0 -2
- cartography/models/aws/sns/topic_subscription.py +74 -0
- cartography/models/aws/sqs/__init__.py +0 -0
- cartography/models/aws/sqs/queue.py +89 -0
- cartography/models/azure/__init__.py +0 -0
- cartography/models/azure/aks_cluster.py +54 -0
- cartography/models/azure/aks_nodepool.py +54 -0
- cartography/models/azure/app_service.py +59 -0
- cartography/models/azure/container_instance.py +57 -0
- cartography/models/azure/cosmosdb/__init__.py +0 -0
- cartography/models/azure/cosmosdb/account.py +77 -0
- cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
- cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
- cartography/models/azure/cosmosdb/cassandratable.py +81 -0
- cartography/models/azure/cosmosdb/corspolicy.py +74 -0
- cartography/models/azure/cosmosdb/dblocation.py +120 -0
- cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
- cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
- cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
- cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
- cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
- cartography/models/azure/cosmosdb/tableresource.py +76 -0
- cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
- cartography/models/azure/data_factory/__init__.py +0 -0
- cartography/models/azure/data_factory/data_factory.py +51 -0
- cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
- cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
- cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
- cartography/models/azure/data_lake_filesystem.py +51 -0
- cartography/models/azure/event_grid_topic.py +57 -0
- cartography/models/azure/function_app.py +59 -0
- cartography/models/azure/load_balancer/__init__.py +0 -0
- cartography/models/azure/load_balancer/load_balancer.py +49 -0
- cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
- cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
- cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
- cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
- cartography/models/azure/logic_apps.py +56 -0
- cartography/models/azure/monitor.py +54 -0
- cartography/models/azure/network_interface.py +112 -0
- cartography/models/azure/network_security_group.py +50 -0
- cartography/models/azure/permission_relationships.py +60 -0
- cartography/models/azure/principal.py +41 -0
- cartography/models/azure/public_ip_address.py +50 -0
- cartography/models/azure/rbac.py +268 -0
- cartography/models/azure/resource_groups.py +52 -0
- cartography/models/azure/security_center.py +50 -0
- cartography/models/azure/sql/__init__.py +0 -0
- cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
- cartography/models/azure/sql/elasticpool.py +77 -0
- cartography/models/azure/sql/failovergroup.py +73 -0
- cartography/models/azure/sql/recoverabledatabase.py +75 -0
- cartography/models/azure/sql/replicationlink.py +81 -0
- cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
- cartography/models/azure/sql/restorepoint.py +74 -0
- cartography/models/azure/sql/serveradadministrator.py +74 -0
- cartography/models/azure/sql/serverdnsalias.py +71 -0
- cartography/models/azure/sql/sqldatabase.py +85 -0
- cartography/models/azure/sql/sqlserver.py +50 -0
- cartography/models/azure/sql/transparentdataencryption.py +76 -0
- cartography/models/azure/storage/__init__.py +0 -0
- cartography/models/azure/storage/account.py +59 -0
- cartography/models/azure/storage/blobcontainer.py +85 -0
- cartography/models/azure/storage/blobservice.py +71 -0
- cartography/models/azure/storage/fileservice.py +71 -0
- cartography/models/azure/storage/fileshare.py +82 -0
- cartography/models/azure/storage/queue.py +71 -0
- cartography/models/azure/storage/queueservice.py +73 -0
- cartography/models/azure/storage/table.py +72 -0
- cartography/models/azure/storage/tableservice.py +73 -0
- cartography/models/azure/subnet.py +101 -0
- cartography/models/azure/subscription.py +47 -0
- cartography/models/azure/tags/__init__.py +0 -0
- cartography/models/azure/tags/storage_tag.py +40 -0
- cartography/models/azure/tags/tag.py +37 -0
- cartography/models/azure/tenant.py +17 -0
- cartography/models/azure/virtual_network.py +49 -0
- cartography/models/azure/vm/__init__.py +0 -0
- cartography/models/azure/vm/datadisk.py +80 -0
- cartography/models/azure/vm/disk.py +55 -0
- cartography/models/azure/vm/snapshot.py +56 -0
- cartography/models/azure/vm/virtualmachine.py +59 -0
- cartography/models/bigfix/bigfix_computer.py +1 -1
- cartography/models/cloudflare/member.py +4 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/nodes.py +15 -2
- cartography/models/core/relationships.py +44 -0
- cartography/models/crowdstrike/hosts.py +1 -1
- cartography/models/digitalocean/droplet.py +2 -0
- cartography/models/duo/endpoint.py +1 -1
- cartography/models/duo/phone.py +2 -2
- cartography/models/duo/user.py +4 -0
- cartography/models/entra/app_role_assignment.py +115 -0
- cartography/models/entra/application.py +49 -0
- cartography/models/entra/entra_user_to_aws_sso.py +41 -0
- cartography/models/entra/group.py +117 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/user.py +42 -51
- cartography/models/gcp/__init__.py +0 -0
- cartography/models/gcp/bigtable/__init__.py +0 -0
- cartography/models/gcp/bigtable/app_profile.py +94 -0
- cartography/models/gcp/bigtable/backup.py +91 -0
- cartography/models/gcp/bigtable/cluster.py +73 -0
- cartography/models/gcp/bigtable/instance.py +52 -0
- cartography/models/gcp/bigtable/table.py +69 -0
- cartography/models/gcp/compute/__init__.py +0 -0
- cartography/models/gcp/compute/subnet.py +74 -0
- cartography/models/gcp/compute/vpc.py +50 -0
- cartography/models/gcp/crm/__init__.py +0 -0
- cartography/models/gcp/crm/folders.py +98 -0
- cartography/models/gcp/crm/organizations.py +21 -0
- cartography/models/gcp/crm/projects.py +100 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/gke.py +69 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/permission_relationships.py +61 -0
- cartography/models/gcp/policy_bindings.py +93 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- cartography/models/github/commits.py +63 -0
- cartography/models/github/dependencies.py +73 -0
- cartography/models/github/manifests.py +49 -0
- cartography/models/github/users.py +10 -0
- cartography/models/googleworkspace/__init__.py +0 -0
- cartography/models/googleworkspace/device.py +132 -0
- cartography/models/googleworkspace/group.py +382 -0
- cartography/models/googleworkspace/oauth_app.py +124 -0
- cartography/models/googleworkspace/tenant.py +30 -0
- cartography/models/googleworkspace/user.py +113 -0
- cartography/models/gsuite/__init__.py +0 -0
- cartography/models/gsuite/group.py +218 -0
- cartography/models/gsuite/tenant.py +29 -0
- cartography/models/gsuite/user.py +107 -0
- cartography/models/kandji/device.py +1 -2
- cartography/models/keycloak/__init__.py +0 -0
- cartography/models/keycloak/authenticationexecution.py +160 -0
- cartography/models/keycloak/authenticationflow.py +54 -0
- cartography/models/keycloak/client.py +179 -0
- cartography/models/keycloak/group.py +101 -0
- cartography/models/keycloak/identityprovider.py +89 -0
- cartography/models/keycloak/organization.py +116 -0
- cartography/models/keycloak/organizationdomain.py +73 -0
- cartography/models/keycloak/realm.py +173 -0
- cartography/models/keycloak/role.py +126 -0
- cartography/models/keycloak/scope.py +73 -0
- cartography/models/keycloak/user.py +55 -0
- cartography/models/kubernetes/__init__.py +0 -0
- cartography/models/kubernetes/clusterrolebindings.py +138 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/clusters.py +26 -0
- cartography/models/kubernetes/containers.py +133 -0
- cartography/models/kubernetes/groups.py +107 -0
- cartography/models/kubernetes/namespaces.py +51 -0
- cartography/models/kubernetes/oidc.py +51 -0
- cartography/models/kubernetes/pods.py +80 -0
- cartography/models/kubernetes/rolebindings.py +159 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/secrets.py +79 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- cartography/models/kubernetes/services.py +108 -0
- cartography/models/kubernetes/users.py +105 -0
- cartography/models/lastpass/user.py +4 -0
- cartography/models/ontology/__init__.py +0 -0
- cartography/models/ontology/device.py +137 -0
- cartography/models/ontology/mapping/__init__.py +76 -0
- cartography/models/ontology/mapping/data/__init__.py +0 -0
- cartography/models/ontology/mapping/data/apikeys.py +93 -0
- cartography/models/ontology/mapping/data/computeinstance.py +95 -0
- cartography/models/ontology/mapping/data/containers.py +88 -0
- cartography/models/ontology/mapping/data/databases.py +182 -0
- cartography/models/ontology/mapping/data/devices.py +194 -0
- cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
- cartography/models/ontology/mapping/data/useraccounts.py +416 -0
- cartography/models/ontology/mapping/data/users.py +63 -0
- cartography/models/ontology/mapping/specs.py +85 -0
- cartography/models/ontology/user.py +51 -0
- cartography/models/openai/adminapikey.py +4 -0
- cartography/models/openai/apikey.py +4 -0
- cartography/models/openai/user.py +4 -0
- cartography/models/scaleway/__init__.py +0 -0
- cartography/models/scaleway/iam/__init__.py +0 -0
- cartography/models/scaleway/iam/apikey.py +100 -0
- cartography/models/scaleway/iam/application.py +52 -0
- cartography/models/scaleway/iam/group.py +95 -0
- cartography/models/scaleway/iam/user.py +64 -0
- cartography/models/scaleway/instance/__init__.py +0 -0
- cartography/models/scaleway/instance/flexibleip.py +52 -0
- cartography/models/scaleway/instance/instance.py +120 -0
- cartography/models/scaleway/organization.py +19 -0
- cartography/models/scaleway/project.py +48 -0
- cartography/models/scaleway/storage/__init__.py +0 -0
- cartography/models/scaleway/storage/snapshot.py +78 -0
- cartography/models/scaleway/storage/volume.py +51 -0
- cartography/models/sentinelone/__init__.py +1 -0
- cartography/models/sentinelone/account.py +40 -0
- cartography/models/sentinelone/agent.py +50 -0
- cartography/models/sentinelone/application.py +44 -0
- cartography/models/sentinelone/application_version.py +96 -0
- cartography/models/sentinelone/cve.py +73 -0
- cartography/models/slack/__init__.py +0 -0
- cartography/models/slack/channels.py +92 -0
- cartography/models/slack/group.py +129 -0
- cartography/models/slack/team.py +22 -0
- cartography/models/slack/user.py +62 -0
- cartography/models/snipeit/asset.py +2 -0
- cartography/models/snipeit/user.py +4 -0
- cartography/models/spacelift/__init__.py +0 -0
- cartography/models/spacelift/cloudtrailevent.py +120 -0
- cartography/models/spacelift/run.py +162 -0
- cartography/models/spacelift/space.py +131 -0
- cartography/models/spacelift/spaceliftaccount.py +31 -0
- cartography/models/spacelift/spaceliftgitcommit.py +157 -0
- cartography/models/spacelift/stack.py +96 -0
- cartography/models/spacelift/user.py +63 -0
- cartography/models/spacelift/worker.py +97 -0
- cartography/models/spacelift/workerpool.py +90 -0
- cartography/models/tailscale/device.py +2 -1
- cartography/models/tailscale/user.py +6 -1
- cartography/models/trivy/__init__.py +0 -0
- cartography/models/trivy/findings.py +66 -0
- cartography/models/trivy/fix.py +66 -0
- cartography/models/trivy/package.py +71 -0
- cartography/rules/README.md +1 -0
- cartography/rules/__init__.py +0 -0
- cartography/rules/cli.py +261 -0
- cartography/rules/data/__init__.py +0 -0
- cartography/rules/data/rules/__init__.py +46 -0
- cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
- cartography/rules/data/rules/compute_instance_exposed.py +51 -0
- cartography/rules/data/rules/database_instance_exposed.py +53 -0
- cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
- cartography/rules/data/rules/identity_administration_privileges.py +100 -0
- cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
- cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
- cartography/rules/data/rules/mfa_missing.py +46 -0
- cartography/rules/data/rules/object_storage_public.py +100 -0
- cartography/rules/data/rules/policy_administration_privileges.py +104 -0
- cartography/rules/data/rules/unmanaged_accounts.py +43 -0
- cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
- cartography/rules/formatters.py +108 -0
- cartography/rules/runners.py +216 -0
- cartography/rules/spec/__init__.py +0 -0
- cartography/rules/spec/model.py +267 -0
- cartography/rules/spec/result.py +38 -0
- cartography/sync.py +25 -5
- cartography/util.py +101 -31
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
- cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
- cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
- cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
- cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
- cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
- cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
- cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
- cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
- cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
- cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
- cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
- cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
- cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
- cartography/intel/gcp/crm.py +0 -355
- cartography/intel/gsuite/api.py +0 -342
- cartography-0.104.0rc2.dist-info/RECORD +0 -455
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
- /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
- /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
- /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
cartography/intel/aws/iam.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import enum
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
+
from collections import namedtuple
|
|
4
5
|
from typing import Any
|
|
5
6
|
from typing import Dict
|
|
6
7
|
from typing import List
|
|
@@ -9,11 +10,27 @@ from typing import Tuple
|
|
|
9
10
|
import boto3
|
|
10
11
|
import neo4j
|
|
11
12
|
|
|
12
|
-
from cartography.
|
|
13
|
+
from cartography.client.core.tx import load
|
|
14
|
+
from cartography.client.core.tx import load_matchlinks
|
|
15
|
+
from cartography.client.core.tx import read_list_of_dicts_tx
|
|
16
|
+
from cartography.client.core.tx import read_list_of_values_tx
|
|
17
|
+
from cartography.graph.job import GraphJob
|
|
13
18
|
from cartography.intel.aws.permission_relationships import principal_allowed_on_resource
|
|
19
|
+
from cartography.models.aws.iam.access_key import AccountAccessKeySchema
|
|
20
|
+
from cartography.models.aws.iam.account_role import AWSAccountAWSRoleSchema
|
|
21
|
+
from cartography.models.aws.iam.federated_principal import AWSFederatedPrincipalSchema
|
|
22
|
+
from cartography.models.aws.iam.group import AWSGroupSchema
|
|
23
|
+
from cartography.models.aws.iam.inline_policy import AWSInlinePolicySchema
|
|
24
|
+
from cartography.models.aws.iam.managed_policy import AWSManagedPolicySchema
|
|
25
|
+
from cartography.models.aws.iam.policy_statement import AWSPolicyStatementSchema
|
|
26
|
+
from cartography.models.aws.iam.role import AWSRoleSchema
|
|
27
|
+
from cartography.models.aws.iam.root_principal import AWSRootPrincipalSchema
|
|
28
|
+
from cartography.models.aws.iam.service_principal import AWSServicePrincipalSchema
|
|
29
|
+
from cartography.models.aws.iam.sts_assumerole_allow import STSAssumeRoleAllowMatchLink
|
|
30
|
+
from cartography.models.aws.iam.user import AWSUserSchema
|
|
14
31
|
from cartography.stats import get_stats_client
|
|
32
|
+
from cartography.util import aws_handle_regions
|
|
15
33
|
from cartography.util import merge_module_sync_metadata
|
|
16
|
-
from cartography.util import run_cleanup_job
|
|
17
34
|
from cartography.util import timeit
|
|
18
35
|
|
|
19
36
|
logger = logging.getLogger(__name__)
|
|
@@ -28,12 +45,32 @@ class PolicyType(enum.Enum):
|
|
|
28
45
|
inline = "inline"
|
|
29
46
|
|
|
30
47
|
|
|
48
|
+
TransformedRoleData = namedtuple(
|
|
49
|
+
"TransformedRoleData",
|
|
50
|
+
[
|
|
51
|
+
"role_data",
|
|
52
|
+
"federated_principals",
|
|
53
|
+
"service_principals",
|
|
54
|
+
"external_aws_accounts",
|
|
55
|
+
],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
TransformedPolicyData = namedtuple(
|
|
59
|
+
"TransformedPolicyData",
|
|
60
|
+
[
|
|
61
|
+
"managed_policies",
|
|
62
|
+
"inline_policies",
|
|
63
|
+
"statements_by_policy_id",
|
|
64
|
+
],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
31
68
|
def get_policy_name_from_arn(arn: str) -> str:
|
|
32
69
|
return arn.split("/")[-1]
|
|
33
70
|
|
|
34
71
|
|
|
35
72
|
@timeit
|
|
36
|
-
def get_group_policies(boto3_session: boto3.
|
|
73
|
+
def get_group_policies(boto3_session: boto3.Session, group_name: str) -> Dict:
|
|
37
74
|
client = boto3_session.client("iam")
|
|
38
75
|
paginator = client.get_paginator("list_group_policies")
|
|
39
76
|
policy_names: List[Dict] = []
|
|
@@ -44,7 +81,7 @@ def get_group_policies(boto3_session: boto3.session.Session, group_name: str) ->
|
|
|
44
81
|
|
|
45
82
|
@timeit
|
|
46
83
|
def get_group_policy_info(
|
|
47
|
-
boto3_session: boto3.
|
|
84
|
+
boto3_session: boto3.Session,
|
|
48
85
|
group_name: str,
|
|
49
86
|
policy_name: str,
|
|
50
87
|
) -> Any:
|
|
@@ -54,7 +91,7 @@ def get_group_policy_info(
|
|
|
54
91
|
|
|
55
92
|
@timeit
|
|
56
93
|
def get_group_membership_data(
|
|
57
|
-
boto3_session: boto3.
|
|
94
|
+
boto3_session: boto3.Session,
|
|
58
95
|
group_name: str,
|
|
59
96
|
) -> Dict:
|
|
60
97
|
client = boto3_session.client("iam")
|
|
@@ -71,8 +108,9 @@ def get_group_membership_data(
|
|
|
71
108
|
|
|
72
109
|
|
|
73
110
|
@timeit
|
|
111
|
+
@aws_handle_regions
|
|
74
112
|
def get_group_policy_data(
|
|
75
|
-
boto3_session: boto3.
|
|
113
|
+
boto3_session: boto3.Session,
|
|
76
114
|
group_list: List[Dict],
|
|
77
115
|
) -> Dict:
|
|
78
116
|
resource_client = boto3_session.resource("iam")
|
|
@@ -89,8 +127,9 @@ def get_group_policy_data(
|
|
|
89
127
|
|
|
90
128
|
|
|
91
129
|
@timeit
|
|
130
|
+
@aws_handle_regions
|
|
92
131
|
def get_group_managed_policy_data(
|
|
93
|
-
boto3_session: boto3.
|
|
132
|
+
boto3_session: boto3.Session,
|
|
94
133
|
group_list: List[Dict],
|
|
95
134
|
) -> Dict:
|
|
96
135
|
resource_client = boto3_session.resource("iam")
|
|
@@ -107,8 +146,9 @@ def get_group_managed_policy_data(
|
|
|
107
146
|
|
|
108
147
|
|
|
109
148
|
@timeit
|
|
149
|
+
@aws_handle_regions
|
|
110
150
|
def get_user_policy_data(
|
|
111
|
-
boto3_session: boto3.
|
|
151
|
+
boto3_session: boto3.Session,
|
|
112
152
|
user_list: List[Dict],
|
|
113
153
|
) -> Dict:
|
|
114
154
|
resource_client = boto3_session.resource("iam")
|
|
@@ -130,8 +170,9 @@ def get_user_policy_data(
|
|
|
130
170
|
|
|
131
171
|
|
|
132
172
|
@timeit
|
|
173
|
+
@aws_handle_regions
|
|
133
174
|
def get_user_managed_policy_data(
|
|
134
|
-
boto3_session: boto3.
|
|
175
|
+
boto3_session: boto3.Session,
|
|
135
176
|
user_list: List[Dict],
|
|
136
177
|
) -> Dict:
|
|
137
178
|
resource_client = boto3_session.resource("iam")
|
|
@@ -153,8 +194,9 @@ def get_user_managed_policy_data(
|
|
|
153
194
|
|
|
154
195
|
|
|
155
196
|
@timeit
|
|
197
|
+
@aws_handle_regions
|
|
156
198
|
def get_role_policy_data(
|
|
157
|
-
boto3_session: boto3.
|
|
199
|
+
boto3_session: boto3.Session,
|
|
158
200
|
role_list: List[Dict],
|
|
159
201
|
) -> Dict:
|
|
160
202
|
resource_client = boto3_session.resource("iam")
|
|
@@ -176,8 +218,9 @@ def get_role_policy_data(
|
|
|
176
218
|
|
|
177
219
|
|
|
178
220
|
@timeit
|
|
221
|
+
@aws_handle_regions
|
|
179
222
|
def get_role_managed_policy_data(
|
|
180
|
-
boto3_session: boto3.
|
|
223
|
+
boto3_session: boto3.Session,
|
|
181
224
|
role_list: List[Dict],
|
|
182
225
|
) -> Dict:
|
|
183
226
|
resource_client = boto3_session.resource("iam")
|
|
@@ -199,7 +242,8 @@ def get_role_managed_policy_data(
|
|
|
199
242
|
|
|
200
243
|
|
|
201
244
|
@timeit
|
|
202
|
-
|
|
245
|
+
@aws_handle_regions
|
|
246
|
+
def get_role_tags(boto3_session: boto3.Session) -> List[Dict]:
|
|
203
247
|
role_list = get_role_list_data(boto3_session)["Roles"]
|
|
204
248
|
resource_client = boto3_session.resource("iam")
|
|
205
249
|
role_tag_data: List[Dict] = []
|
|
@@ -221,7 +265,7 @@ def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
|
|
|
221
265
|
|
|
222
266
|
|
|
223
267
|
@timeit
|
|
224
|
-
def get_user_list_data(boto3_session: boto3.
|
|
268
|
+
def get_user_list_data(boto3_session: boto3.Session) -> Dict:
|
|
225
269
|
client = boto3_session.client("iam")
|
|
226
270
|
|
|
227
271
|
paginator = client.get_paginator("list_users")
|
|
@@ -232,7 +276,7 @@ def get_user_list_data(boto3_session: boto3.session.Session) -> Dict:
|
|
|
232
276
|
|
|
233
277
|
|
|
234
278
|
@timeit
|
|
235
|
-
def get_group_list_data(boto3_session: boto3.
|
|
279
|
+
def get_group_list_data(boto3_session: boto3.Session) -> Dict:
|
|
236
280
|
client = boto3_session.client("iam")
|
|
237
281
|
paginator = client.get_paginator("list_groups")
|
|
238
282
|
groups: List[Dict] = []
|
|
@@ -242,7 +286,7 @@ def get_group_list_data(boto3_session: boto3.session.Session) -> Dict:
|
|
|
242
286
|
|
|
243
287
|
|
|
244
288
|
@timeit
|
|
245
|
-
def get_role_list_data(boto3_session: boto3.
|
|
289
|
+
def get_role_list_data(boto3_session: boto3.Session) -> Dict:
|
|
246
290
|
client = boto3_session.client("iam")
|
|
247
291
|
paginator = client.get_paginator("list_roles")
|
|
248
292
|
roles: List[Dict] = []
|
|
@@ -251,9 +295,33 @@ def get_role_list_data(boto3_session: boto3.session.Session) -> Dict:
|
|
|
251
295
|
return {"Roles": roles}
|
|
252
296
|
|
|
253
297
|
|
|
298
|
+
@timeit
|
|
299
|
+
def get_user_access_keys_data(
|
|
300
|
+
boto3_session: boto3.Session,
|
|
301
|
+
users: list[dict[str, Any]],
|
|
302
|
+
) -> dict[str, list[dict[str, Any]]]:
|
|
303
|
+
"""
|
|
304
|
+
Get access key data for all users.
|
|
305
|
+
Returns a dict mapping user ARN to list of access key data.
|
|
306
|
+
"""
|
|
307
|
+
user_access_keys = {}
|
|
308
|
+
|
|
309
|
+
for user in users:
|
|
310
|
+
username = user["name"]
|
|
311
|
+
user_arn = user["arn"]
|
|
312
|
+
|
|
313
|
+
access_keys = get_account_access_key_data(boto3_session, username)
|
|
314
|
+
if access_keys and "AccessKeyMetadata" in access_keys:
|
|
315
|
+
user_access_keys[user_arn] = access_keys["AccessKeyMetadata"]
|
|
316
|
+
else:
|
|
317
|
+
user_access_keys[user_arn] = []
|
|
318
|
+
|
|
319
|
+
return user_access_keys
|
|
320
|
+
|
|
321
|
+
|
|
254
322
|
@timeit
|
|
255
323
|
def get_account_access_key_data(
|
|
256
|
-
boto3_session: boto3.
|
|
324
|
+
boto3_session: boto3.Session,
|
|
257
325
|
username: str,
|
|
258
326
|
) -> Dict:
|
|
259
327
|
client = boto3_session.client("iam")
|
|
@@ -280,223 +348,256 @@ def get_account_access_key_data(
|
|
|
280
348
|
|
|
281
349
|
|
|
282
350
|
@timeit
|
|
283
|
-
def
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
current_aws_account_id: str,
|
|
287
|
-
aws_update_tag: int,
|
|
288
|
-
) -> None:
|
|
289
|
-
ingest_user = """
|
|
290
|
-
MERGE (unode:AWSUser{arn: $ARN})
|
|
291
|
-
ON CREATE SET unode:AWSPrincipal, unode.userid = $USERID, unode.firstseen = timestamp(),
|
|
292
|
-
unode.createdate = $CREATE_DATE
|
|
293
|
-
SET unode.name = $USERNAME, unode.path = $PATH, unode.passwordlastused = $PASSWORD_LASTUSED,
|
|
294
|
-
unode.lastupdated = $aws_update_tag
|
|
295
|
-
WITH unode
|
|
296
|
-
MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
|
|
297
|
-
MERGE (aa)-[r:RESOURCE]->(unode)
|
|
298
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
299
|
-
SET r.lastupdated = $aws_update_tag
|
|
351
|
+
def get_group_memberships(
|
|
352
|
+
boto3_session: boto3.Session, groups: list[dict[str, Any]]
|
|
353
|
+
) -> dict[str, list[str]]:
|
|
300
354
|
"""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
355
|
+
Get membership data for all groups.
|
|
356
|
+
Returns a dict mapping group ARN to list of user ARNs.
|
|
357
|
+
"""
|
|
358
|
+
memberships = {}
|
|
359
|
+
for group in groups:
|
|
360
|
+
try:
|
|
361
|
+
membership_data = get_group_membership_data(
|
|
362
|
+
boto3_session, group["GroupName"]
|
|
363
|
+
)
|
|
364
|
+
if membership_data and "Users" in membership_data:
|
|
365
|
+
memberships[group["Arn"]] = [
|
|
366
|
+
user["Arn"] for user in membership_data["Users"]
|
|
367
|
+
]
|
|
368
|
+
else:
|
|
369
|
+
memberships[group["Arn"]] = []
|
|
370
|
+
except Exception:
|
|
371
|
+
logger.warning(
|
|
372
|
+
f"Could not get membership data for group {group['GroupName']}",
|
|
373
|
+
exc_info=True,
|
|
374
|
+
)
|
|
375
|
+
memberships[group["Arn"]] = []
|
|
376
|
+
|
|
377
|
+
return memberships
|
|
314
378
|
|
|
315
379
|
|
|
316
380
|
@timeit
|
|
317
|
-
def
|
|
381
|
+
def get_policies_for_principal(
|
|
318
382
|
neo4j_session: neo4j.Session,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
MERGE (aa)-[r:RESOURCE]->(gnode)
|
|
330
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
331
|
-
SET r.lastupdated = $aws_update_tag
|
|
383
|
+
principal_arn: str,
|
|
384
|
+
) -> Dict:
|
|
385
|
+
get_policy_query = """
|
|
386
|
+
MATCH
|
|
387
|
+
(principal:AWSPrincipal{arn:$Arn})-[:POLICY]->
|
|
388
|
+
(policy:AWSPolicy)-[:STATEMENT]->
|
|
389
|
+
(statements:AWSPolicyStatement)
|
|
390
|
+
RETURN
|
|
391
|
+
DISTINCT policy.id AS policy_id,
|
|
392
|
+
COLLECT(DISTINCT statements) AS statements
|
|
332
393
|
"""
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
GROUP_NAME=group["GroupName"],
|
|
341
|
-
PATH=group["Path"],
|
|
342
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
343
|
-
aws_update_tag=aws_update_tag,
|
|
344
|
-
)
|
|
394
|
+
results = neo4j_session.execute_read(
|
|
395
|
+
read_list_of_dicts_tx,
|
|
396
|
+
get_policy_query,
|
|
397
|
+
Arn=principal_arn,
|
|
398
|
+
)
|
|
399
|
+
policies = {r["policy_id"]: r["statements"] for r in results}
|
|
400
|
+
return policies
|
|
345
401
|
|
|
346
402
|
|
|
347
|
-
def
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return principal_entries
|
|
403
|
+
def transform_users(users: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
404
|
+
user_data = []
|
|
405
|
+
for user in users:
|
|
406
|
+
user_record = {
|
|
407
|
+
"arn": user["Arn"],
|
|
408
|
+
"userid": user["UserId"],
|
|
409
|
+
"name": user["UserName"],
|
|
410
|
+
"path": user["Path"],
|
|
411
|
+
"createdate": str(user["CreateDate"]),
|
|
412
|
+
"passwordlastused": str(user.get("PasswordLastUsed", "")),
|
|
413
|
+
}
|
|
414
|
+
user_data.append(user_record)
|
|
360
415
|
|
|
416
|
+
return user_data
|
|
361
417
|
|
|
362
|
-
@timeit
|
|
363
|
-
def load_roles(
|
|
364
|
-
neo4j_session: neo4j.Session,
|
|
365
|
-
roles: List[Dict],
|
|
366
|
-
current_aws_account_id: str,
|
|
367
|
-
aws_update_tag: int,
|
|
368
|
-
) -> None:
|
|
369
|
-
ingest_role = """
|
|
370
|
-
MERGE (rnode:AWSPrincipal{arn: $Arn})
|
|
371
|
-
ON CREATE SET rnode.firstseen = timestamp()
|
|
372
|
-
SET
|
|
373
|
-
rnode:AWSRole,
|
|
374
|
-
rnode.roleid = $RoleId,
|
|
375
|
-
rnode.createdate = $CreateDate,
|
|
376
|
-
rnode.name = $RoleName,
|
|
377
|
-
rnode.path = $Path,
|
|
378
|
-
rnode.lastupdated = $aws_update_tag
|
|
379
|
-
WITH rnode
|
|
380
|
-
MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
|
|
381
|
-
MERGE (aa)-[r:RESOURCE]->(rnode)
|
|
382
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
383
|
-
SET r.lastupdated = $aws_update_tag
|
|
384
|
-
"""
|
|
385
418
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
419
|
+
def transform_groups(
|
|
420
|
+
groups: list[dict[str, Any]], group_memberships: dict[str, list[str]]
|
|
421
|
+
) -> list[dict[str, Any]]:
|
|
422
|
+
group_data = []
|
|
423
|
+
for group in groups:
|
|
424
|
+
group_record = {
|
|
425
|
+
"arn": group["Arn"],
|
|
426
|
+
"groupid": group["GroupId"],
|
|
427
|
+
"name": group["GroupName"],
|
|
428
|
+
"path": group["Path"],
|
|
429
|
+
"createdate": str(group["CreateDate"]),
|
|
430
|
+
"user_arns": group_memberships.get(group["Arn"], []),
|
|
431
|
+
}
|
|
432
|
+
group_data.append(group_record)
|
|
433
|
+
|
|
434
|
+
return group_data
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def transform_access_keys(
|
|
438
|
+
user_access_keys: dict[str, list[dict[str, Any]]],
|
|
439
|
+
) -> list[dict[str, Any]]:
|
|
440
|
+
access_key_data = []
|
|
441
|
+
for user_arn, access_keys in user_access_keys.items():
|
|
442
|
+
for access_key in access_keys:
|
|
443
|
+
if access_key.get("AccessKeyId"):
|
|
444
|
+
access_key_record = {
|
|
445
|
+
"accesskeyid": access_key["AccessKeyId"],
|
|
446
|
+
"createdate": str(access_key["CreateDate"]),
|
|
447
|
+
"status": access_key["Status"],
|
|
448
|
+
"lastuseddate": str(access_key.get("LastUsedDate", "")),
|
|
449
|
+
"lastusedservice": access_key.get("LastUsedService", ""),
|
|
450
|
+
"lastusedregion": access_key.get("LastUsedRegion", ""),
|
|
451
|
+
"user_arn": user_arn, # For the sub-resource relationship
|
|
452
|
+
}
|
|
453
|
+
access_key_data.append(access_key_record)
|
|
454
|
+
|
|
455
|
+
return access_key_data
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def transform_role_trust_policies(
|
|
459
|
+
roles: list[dict[str, Any]], current_aws_account_id: str
|
|
460
|
+
) -> TransformedRoleData:
|
|
395
461
|
"""
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
#
|
|
399
|
-
# we are agnostic here if this is the AWSAccount is part of the sync scope or
|
|
400
|
-
# a foreign AWS account that contains a trusted principal. The account could also be inscope
|
|
401
|
-
# but not sync yet.
|
|
402
|
-
# - The inscope attribute - set when the account is being sync.
|
|
403
|
-
# - The foreign attribute - the attribute assignment logic is in aws_foreign_accounts.json analysis job
|
|
404
|
-
# - Why seperate statement is needed - the arn may point to service level principals ex - ec2.amazonaws.com
|
|
405
|
-
ingest_spnmap_statement = """
|
|
406
|
-
MERGE (aa:AWSAccount{id: $SpnAccountId})
|
|
407
|
-
ON CREATE SET aa.firstseen = timestamp()
|
|
408
|
-
SET aa.lastupdated = $aws_update_tag
|
|
409
|
-
WITH aa
|
|
410
|
-
MATCH (spnnode:AWSPrincipal{arn: $SpnArn})
|
|
411
|
-
WITH spnnode, aa
|
|
412
|
-
MERGE (aa)-[r:RESOURCE]->(spnnode)
|
|
413
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
462
|
+
Processes AWS role assumption policy documents in the list_roles response.
|
|
463
|
+
Returns a TransformedRoleData object containing the role data, federated principals, service principals, and external AWS accounts.
|
|
414
464
|
"""
|
|
465
|
+
role_data: list[dict[str, Any]] = []
|
|
466
|
+
federated_principals: list[dict[str, Any]] = []
|
|
467
|
+
service_principals: list[dict[str, Any]] = []
|
|
468
|
+
external_aws_accounts: list[dict[str, Any]] = []
|
|
415
469
|
|
|
416
|
-
# TODO support conditions
|
|
417
|
-
logger.info(f"Loading {len(roles)} IAM roles to the graph.")
|
|
418
470
|
for role in roles:
|
|
419
|
-
|
|
420
|
-
ingest_role,
|
|
421
|
-
Arn=role["Arn"],
|
|
422
|
-
RoleId=role["RoleId"],
|
|
423
|
-
CreateDate=str(role["CreateDate"]),
|
|
424
|
-
RoleName=role["RoleName"],
|
|
425
|
-
Path=role["Path"],
|
|
426
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
427
|
-
aws_update_tag=aws_update_tag,
|
|
428
|
-
)
|
|
471
|
+
role_arn = role["Arn"]
|
|
429
472
|
|
|
473
|
+
# List of principals of type "AWS" that this role trusts
|
|
474
|
+
trusted_aws_principals = set()
|
|
475
|
+
# Process each statement in the assume role policy document
|
|
476
|
+
# TODO support conditions
|
|
430
477
|
for statement in role["AssumeRolePolicyDocument"]["Statement"]:
|
|
478
|
+
|
|
431
479
|
principal_entries = _parse_principal_entries(statement["Principal"])
|
|
432
|
-
for principal_type,
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
480
|
+
for principal_type, principal_arn in principal_entries:
|
|
481
|
+
if principal_type == "Federated":
|
|
482
|
+
# Add this to list of federated nodes to create
|
|
483
|
+
account_id = get_account_from_arn(principal_arn)
|
|
484
|
+
federated_principals.append(
|
|
485
|
+
{
|
|
486
|
+
"arn": principal_arn,
|
|
487
|
+
"type": "Federated",
|
|
488
|
+
"other_account_id": (
|
|
489
|
+
account_id
|
|
490
|
+
if account_id != current_aws_account_id
|
|
491
|
+
else None
|
|
492
|
+
),
|
|
493
|
+
"role_arn": role_arn,
|
|
494
|
+
}
|
|
495
|
+
)
|
|
496
|
+
trusted_aws_principals.add(principal_arn)
|
|
497
|
+
elif principal_type == "Service":
|
|
498
|
+
# Add to the list of service nodes to create
|
|
499
|
+
service_principals.append(
|
|
500
|
+
{
|
|
501
|
+
"arn": principal_arn,
|
|
502
|
+
"type": "Service",
|
|
503
|
+
}
|
|
447
504
|
)
|
|
505
|
+
# Service principals are global so there is no account id.
|
|
506
|
+
trusted_aws_principals.add(principal_arn)
|
|
507
|
+
elif principal_type == "AWS":
|
|
508
|
+
if "root" in principal_arn:
|
|
509
|
+
# The current principal trusts a root principal.
|
|
510
|
+
|
|
511
|
+
# First check if the root principal is in a different account than the current one.
|
|
512
|
+
# Add what we know about that account to the graph.
|
|
513
|
+
account_id = get_account_from_arn(principal_arn)
|
|
514
|
+
if account_id != current_aws_account_id:
|
|
515
|
+
external_aws_accounts.append({"id": account_id})
|
|
516
|
+
trusted_aws_principals.add(principal_arn)
|
|
517
|
+
else:
|
|
518
|
+
# This should not happen but who knows.
|
|
519
|
+
logger.warning(f"Unknown principal type: {principal_type}")
|
|
520
|
+
|
|
521
|
+
role_record = {
|
|
522
|
+
"arn": role["Arn"],
|
|
523
|
+
"roleid": role["RoleId"],
|
|
524
|
+
"name": role["RoleName"],
|
|
525
|
+
"path": role["Path"],
|
|
526
|
+
"createdate": str(role["CreateDate"]),
|
|
527
|
+
"trusted_aws_principals": list(trusted_aws_principals),
|
|
528
|
+
"account_id": get_account_from_arn(role["Arn"]),
|
|
529
|
+
}
|
|
530
|
+
role_data.append(role_record)
|
|
531
|
+
|
|
532
|
+
return TransformedRoleData(
|
|
533
|
+
role_data=role_data,
|
|
534
|
+
federated_principals=federated_principals,
|
|
535
|
+
service_principals=service_principals,
|
|
536
|
+
external_aws_accounts=external_aws_accounts,
|
|
537
|
+
)
|
|
448
538
|
|
|
449
539
|
|
|
450
540
|
@timeit
|
|
451
|
-
def
|
|
541
|
+
def load_users(
|
|
452
542
|
neo4j_session: neo4j.Session,
|
|
453
|
-
|
|
543
|
+
users: List[Dict],
|
|
544
|
+
current_aws_account_id: str,
|
|
454
545
|
aws_update_tag: int,
|
|
455
546
|
) -> None:
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
WITH user, group
|
|
464
|
-
MATCH (group)-[:POLICY]->(policy:AWSPolicy)
|
|
465
|
-
MERGE (user)-[r2:POLICY]->(policy)
|
|
466
|
-
SET r2.lastupdated = $aws_update_tag
|
|
467
|
-
"""
|
|
547
|
+
load(
|
|
548
|
+
neo4j_session,
|
|
549
|
+
AWSUserSchema(),
|
|
550
|
+
users,
|
|
551
|
+
lastupdated=aws_update_tag,
|
|
552
|
+
AWS_ID=current_aws_account_id,
|
|
553
|
+
)
|
|
468
554
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
555
|
+
|
|
556
|
+
@timeit
|
|
557
|
+
def load_groups(
|
|
558
|
+
neo4j_session: neo4j.Session,
|
|
559
|
+
groups: List[Dict],
|
|
560
|
+
current_aws_account_id: str,
|
|
561
|
+
aws_update_tag: int,
|
|
562
|
+
) -> None:
|
|
563
|
+
load(
|
|
564
|
+
neo4j_session,
|
|
565
|
+
AWSGroupSchema(),
|
|
566
|
+
groups,
|
|
567
|
+
lastupdated=aws_update_tag,
|
|
568
|
+
AWS_ID=current_aws_account_id,
|
|
569
|
+
)
|
|
478
570
|
|
|
479
571
|
|
|
480
572
|
@timeit
|
|
481
|
-
def
|
|
573
|
+
def load_access_keys(
|
|
482
574
|
neo4j_session: neo4j.Session,
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
"""
|
|
494
|
-
results = neo4j_session.run(
|
|
495
|
-
get_policy_query,
|
|
496
|
-
Arn=principal_arn,
|
|
575
|
+
access_keys: List[Dict],
|
|
576
|
+
aws_update_tag: int,
|
|
577
|
+
current_aws_account_id: str,
|
|
578
|
+
) -> None:
|
|
579
|
+
load(
|
|
580
|
+
neo4j_session,
|
|
581
|
+
AccountAccessKeySchema(),
|
|
582
|
+
access_keys,
|
|
583
|
+
lastupdated=aws_update_tag,
|
|
584
|
+
AWS_ID=current_aws_account_id,
|
|
497
585
|
)
|
|
498
|
-
|
|
499
|
-
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _parse_principal_entries(principal: Dict) -> List[Tuple[Any, Any]]:
|
|
589
|
+
"""
|
|
590
|
+
Returns a list of tuples of the form (principal_type, principal_value)
|
|
591
|
+
e.g. [('AWS', 'example-role-name'), ('Service', 'example-service')]
|
|
592
|
+
"""
|
|
593
|
+
principal_entries = []
|
|
594
|
+
for principal_type in principal:
|
|
595
|
+
principal_values = principal[principal_type]
|
|
596
|
+
if not isinstance(principal_values, list):
|
|
597
|
+
principal_values = [principal_values]
|
|
598
|
+
for principal_value in principal_values:
|
|
599
|
+
principal_entries.append((principal_type, principal_value))
|
|
600
|
+
return principal_entries
|
|
500
601
|
|
|
501
602
|
|
|
502
603
|
@timeit
|
|
@@ -507,88 +608,53 @@ def sync_assumerole_relationships(
|
|
|
507
608
|
common_job_parameters: Dict,
|
|
508
609
|
) -> None:
|
|
509
610
|
# Must be called after load_role
|
|
510
|
-
# Computes and syncs the
|
|
611
|
+
# Computes and syncs the STS_ASSUMEROLE_ALLOW relationship
|
|
511
612
|
logger.info(
|
|
512
613
|
"Syncing assume role mappings for account '%s'.",
|
|
513
614
|
current_aws_account_id,
|
|
514
615
|
)
|
|
515
616
|
query_potential_matches = """
|
|
516
617
|
MATCH (:AWSAccount{id:$AccountId})-[:RESOURCE]->(target:AWSRole)-[:TRUSTS_AWS_PRINCIPAL]->(source:AWSPrincipal)
|
|
517
|
-
WHERE NOT source
|
|
518
|
-
AND NOT source
|
|
519
|
-
AND NOT source
|
|
520
|
-
RETURN target.arn AS target_arn,
|
|
521
|
-
source.arn AS source_arn
|
|
522
|
-
"""
|
|
523
|
-
|
|
524
|
-
ingest_policies_assume_role = """
|
|
525
|
-
MATCH (source:AWSPrincipal{arn: $SourceArn})
|
|
526
|
-
WITH source
|
|
527
|
-
MATCH (role:AWSRole{arn: $TargetArn})
|
|
528
|
-
WITH role, source
|
|
529
|
-
MERGE (source)-[r:STS_ASSUMEROLE_ALLOW]->(role)
|
|
530
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
531
|
-
SET r.lastupdated = $aws_update_tag
|
|
618
|
+
WHERE NOT source:AWSRootPrincipal
|
|
619
|
+
AND NOT source:AWSServicePrincipal
|
|
620
|
+
AND NOT source:AWSFederatedPrincipal
|
|
621
|
+
RETURN target.arn AS target_arn, source.arn AS source_arn
|
|
532
622
|
"""
|
|
533
|
-
|
|
534
|
-
|
|
623
|
+
results = neo4j_session.execute_read(
|
|
624
|
+
read_list_of_dicts_tx,
|
|
535
625
|
query_potential_matches,
|
|
536
626
|
AccountId=current_aws_account_id,
|
|
537
627
|
)
|
|
538
|
-
|
|
539
|
-
|
|
628
|
+
|
|
629
|
+
# Filter potential matches to only those where the source principal has sts:AssumeRole permission
|
|
630
|
+
valid_matches = []
|
|
631
|
+
for result in results:
|
|
632
|
+
source_arn = result["source_arn"]
|
|
633
|
+
target_arn = result["target_arn"]
|
|
540
634
|
policies = get_policies_for_principal(neo4j_session, source_arn)
|
|
541
635
|
if principal_allowed_on_resource(policies, target_arn, ["sts:AssumeRole"]):
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
636
|
+
valid_matches.append(
|
|
637
|
+
{
|
|
638
|
+
"source_arn": source_arn,
|
|
639
|
+
"target_arn": target_arn,
|
|
640
|
+
}
|
|
547
641
|
)
|
|
548
|
-
|
|
549
|
-
|
|
642
|
+
|
|
643
|
+
load_matchlinks(
|
|
550
644
|
neo4j_session,
|
|
551
|
-
|
|
645
|
+
STSAssumeRoleAllowMatchLink(),
|
|
646
|
+
valid_matches,
|
|
647
|
+
lastupdated=aws_update_tag,
|
|
648
|
+
_sub_resource_label="AWSAccount",
|
|
649
|
+
_sub_resource_id=current_aws_account_id,
|
|
552
650
|
)
|
|
553
651
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
) -> None:
|
|
561
|
-
# TODO change the node label to reflect that this is a user access key, not an account access key
|
|
562
|
-
ingest_account_key = """
|
|
563
|
-
MATCH (user:AWSUser{arn: $UserARN})
|
|
564
|
-
WITH user
|
|
565
|
-
MERGE (key:AccountAccessKey{accesskeyid: $AccessKeyId})
|
|
566
|
-
ON CREATE SET key.firstseen = timestamp(), key.createdate = $CreateDate
|
|
567
|
-
SET key.status = $Status,
|
|
568
|
-
key.lastupdated = $aws_update_tag,
|
|
569
|
-
key.lastuseddate = $LastUsedDate,
|
|
570
|
-
key.lastusedservice = $LastUsedService,
|
|
571
|
-
key.lastusedregion = $LastUsedRegion
|
|
572
|
-
WITH user,key
|
|
573
|
-
MERGE (user)-[r:AWS_ACCESS_KEY]->(key)
|
|
574
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
575
|
-
SET r.lastupdated = $aws_update_tag
|
|
576
|
-
"""
|
|
577
|
-
|
|
578
|
-
for arn, access_keys in user_access_keys.items():
|
|
579
|
-
for key in access_keys["AccessKeyMetadata"]:
|
|
580
|
-
if key.get("AccessKeyId"):
|
|
581
|
-
neo4j_session.run(
|
|
582
|
-
ingest_account_key,
|
|
583
|
-
UserARN=arn,
|
|
584
|
-
AccessKeyId=key["AccessKeyId"],
|
|
585
|
-
CreateDate=str(key["CreateDate"]),
|
|
586
|
-
Status=key["Status"],
|
|
587
|
-
LastUsedDate=key["LastUsedDate"],
|
|
588
|
-
LastUsedService=key["LastUsedService"],
|
|
589
|
-
LastUsedRegion=key["LastUsedRegion"],
|
|
590
|
-
aws_update_tag=aws_update_tag,
|
|
591
|
-
)
|
|
652
|
+
GraphJob.from_matchlink(
|
|
653
|
+
STSAssumeRoleAllowMatchLink(),
|
|
654
|
+
sub_resource_label="AWSAccount",
|
|
655
|
+
sub_resource_id=current_aws_account_id,
|
|
656
|
+
update_tag=aws_update_tag,
|
|
657
|
+
).run(neo4j_session)
|
|
592
658
|
|
|
593
659
|
|
|
594
660
|
def ensure_list(obj: Any) -> List[Any]:
|
|
@@ -597,304 +663,460 @@ def ensure_list(obj: Any) -> List[Any]:
|
|
|
597
663
|
return obj
|
|
598
664
|
|
|
599
665
|
|
|
600
|
-
def _transform_policy_statements(
|
|
666
|
+
def _transform_policy_statements(
|
|
667
|
+
statements: Any, policy_id: str
|
|
668
|
+
) -> list[dict[str, Any]]:
|
|
669
|
+
result: List[Dict[str, Any]] = []
|
|
601
670
|
count = 1
|
|
671
|
+
|
|
602
672
|
if not isinstance(statements, list):
|
|
603
673
|
statements = [statements]
|
|
674
|
+
|
|
604
675
|
for stmt in statements:
|
|
676
|
+
# Determine statement ID
|
|
605
677
|
if "Sid" in stmt and stmt["Sid"]:
|
|
606
678
|
statement_id = stmt["Sid"]
|
|
607
679
|
else:
|
|
608
680
|
statement_id = count
|
|
609
681
|
count += 1
|
|
610
682
|
|
|
611
|
-
|
|
683
|
+
transformed_stmt = {
|
|
684
|
+
"id": f"{policy_id}/statement/{statement_id}",
|
|
685
|
+
"policy_id": policy_id, # For the relationship to AWSPolicy
|
|
686
|
+
"Effect": stmt.get("Effect"),
|
|
687
|
+
"Sid": stmt.get("Sid"),
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
# Handle list fields
|
|
612
691
|
if "Resource" in stmt:
|
|
613
|
-
|
|
692
|
+
transformed_stmt["Resource"] = ensure_list(stmt["Resource"])
|
|
614
693
|
if "Action" in stmt:
|
|
615
|
-
|
|
694
|
+
transformed_stmt["Action"] = ensure_list(stmt["Action"])
|
|
616
695
|
if "NotAction" in stmt:
|
|
617
|
-
|
|
696
|
+
transformed_stmt["NotAction"] = ensure_list(stmt["NotAction"])
|
|
618
697
|
if "NotResource" in stmt:
|
|
619
|
-
|
|
698
|
+
transformed_stmt["NotResource"] = ensure_list(stmt["NotResource"])
|
|
620
699
|
if "Condition" in stmt:
|
|
621
|
-
|
|
622
|
-
|
|
700
|
+
transformed_stmt["Condition"] = json.dumps(ensure_list(stmt["Condition"]))
|
|
701
|
+
|
|
702
|
+
result.append(transformed_stmt)
|
|
703
|
+
|
|
704
|
+
return result
|
|
623
705
|
|
|
624
706
|
|
|
625
|
-
def transform_policy_data(
|
|
707
|
+
def transform_policy_data(
|
|
708
|
+
policy_map: dict[str, dict[str, Any]], policy_type: str
|
|
709
|
+
) -> TransformedPolicyData:
|
|
710
|
+
"""
|
|
711
|
+
Processes AWS IAM policy documents. Returns a TransformedPolicyData object containing the managed policies, inline policies, and statements by policy id -- all ready to be loaded to the graph.
|
|
712
|
+
"""
|
|
713
|
+
# First pass: collect all policies and their principals
|
|
714
|
+
policy_to_principals: dict[str, set[str]] = {}
|
|
715
|
+
policy_to_statements: dict[str, list[dict[str, Any]]] = {}
|
|
716
|
+
policy_to_name: dict[str, str] = {}
|
|
717
|
+
|
|
626
718
|
for principal_arn, policy_statement_map in policy_map.items():
|
|
627
|
-
logger.debug(
|
|
628
|
-
f"Transforming IAM {policy_type} policies for principal {principal_arn}",
|
|
629
|
-
)
|
|
630
719
|
for policy_key, statements in policy_statement_map.items():
|
|
631
720
|
policy_id = (
|
|
632
|
-
transform_policy_id(
|
|
633
|
-
principal_arn,
|
|
634
|
-
policy_type,
|
|
635
|
-
policy_key,
|
|
636
|
-
)
|
|
721
|
+
transform_policy_id(principal_arn, policy_type, policy_key)
|
|
637
722
|
if policy_type == PolicyType.inline.value
|
|
638
723
|
else policy_key
|
|
639
724
|
)
|
|
640
|
-
|
|
725
|
+
policy_name = (
|
|
726
|
+
policy_key
|
|
727
|
+
if policy_type == PolicyType.inline.value
|
|
728
|
+
else get_policy_name_from_arn(policy_key)
|
|
729
|
+
)
|
|
730
|
+
# Map policy id to the principal arns that have it
|
|
731
|
+
if policy_id not in policy_to_principals:
|
|
732
|
+
policy_to_principals[policy_id] = set()
|
|
733
|
+
policy_to_principals[policy_id].add(principal_arn)
|
|
734
|
+
|
|
735
|
+
# Map policy id to policy name
|
|
736
|
+
policy_to_name[policy_id] = policy_name
|
|
737
|
+
|
|
738
|
+
# Transform and store statements
|
|
739
|
+
transformed_statements = _transform_policy_statements(
|
|
641
740
|
statements,
|
|
642
741
|
policy_id,
|
|
643
742
|
)
|
|
743
|
+
policy_to_statements[policy_id] = transformed_statements
|
|
744
|
+
|
|
745
|
+
# Second pass: create consolidated policy data
|
|
746
|
+
managed_policy_data = []
|
|
747
|
+
inline_policy_data = []
|
|
748
|
+
|
|
749
|
+
for policy_id, principal_arns in policy_to_principals.items():
|
|
750
|
+
policy_name = policy_to_name[policy_id]
|
|
751
|
+
|
|
752
|
+
policy_data = {
|
|
753
|
+
"id": policy_id,
|
|
754
|
+
"name": policy_name,
|
|
755
|
+
"type": policy_type,
|
|
756
|
+
# AWS inline policies don't have arns
|
|
757
|
+
"arn": policy_id if policy_type == PolicyType.managed.value else None,
|
|
758
|
+
"principal_arns": list(principal_arns),
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if policy_type == PolicyType.inline.value:
|
|
762
|
+
inline_policy_data.append(policy_data)
|
|
763
|
+
elif policy_type == PolicyType.managed.value:
|
|
764
|
+
managed_policy_data.append(policy_data)
|
|
765
|
+
else:
|
|
766
|
+
# This really should never happen so just explicitly having a `pass` here.
|
|
767
|
+
pass
|
|
768
|
+
|
|
769
|
+
return TransformedPolicyData(
|
|
770
|
+
managed_policies=managed_policy_data,
|
|
771
|
+
inline_policies=inline_policy_data,
|
|
772
|
+
statements_by_policy_id=policy_to_statements,
|
|
773
|
+
)
|
|
644
774
|
|
|
645
775
|
|
|
646
776
|
def transform_policy_id(principal_arn: str, policy_type: str, name: str) -> str:
|
|
647
777
|
return f"{principal_arn}/{policy_type}_policy/{name}"
|
|
648
778
|
|
|
649
779
|
|
|
650
|
-
def
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
principal_arn: str,
|
|
780
|
+
def _load_policy(
|
|
781
|
+
neo4j_session: neo4j.Session,
|
|
782
|
+
managed_policy_data: list[dict[str, Any]],
|
|
783
|
+
inline_policy_data: list[dict[str, Any]],
|
|
784
|
+
account_id: str,
|
|
656
785
|
aws_update_tag: int,
|
|
657
786
|
) -> None:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
tx.run(
|
|
671
|
-
ingest_policy,
|
|
672
|
-
PolicyId=policy_id,
|
|
673
|
-
PolicyName=policy_name,
|
|
674
|
-
PolicyType=policy_type,
|
|
675
|
-
PrincipalArn=principal_arn,
|
|
676
|
-
aws_update_tag=aws_update_tag,
|
|
787
|
+
load(
|
|
788
|
+
neo4j_session,
|
|
789
|
+
AWSManagedPolicySchema(),
|
|
790
|
+
managed_policy_data,
|
|
791
|
+
lastupdated=aws_update_tag,
|
|
792
|
+
)
|
|
793
|
+
load(
|
|
794
|
+
neo4j_session,
|
|
795
|
+
AWSInlinePolicySchema(),
|
|
796
|
+
inline_policy_data,
|
|
797
|
+
lastupdated=aws_update_tag,
|
|
798
|
+
AWS_ID=account_id,
|
|
677
799
|
)
|
|
678
800
|
|
|
679
801
|
|
|
680
802
|
@timeit
|
|
681
|
-
def
|
|
803
|
+
def load_policy_statements(
|
|
682
804
|
neo4j_session: neo4j.Session,
|
|
683
|
-
|
|
684
|
-
policy_name: str,
|
|
685
|
-
policy_type: str,
|
|
686
|
-
principal_arn: str,
|
|
805
|
+
statements: list[dict[str, Any]],
|
|
687
806
|
aws_update_tag: int,
|
|
688
807
|
) -> None:
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
aws_update_tag,
|
|
808
|
+
load(
|
|
809
|
+
neo4j_session,
|
|
810
|
+
AWSPolicyStatementSchema(),
|
|
811
|
+
statements,
|
|
812
|
+
lastupdated=aws_update_tag,
|
|
813
|
+
POLICY_ID=statements[0]["policy_id"],
|
|
696
814
|
)
|
|
697
815
|
|
|
698
816
|
|
|
699
817
|
@timeit
|
|
700
|
-
def
|
|
818
|
+
def _load_policy_statements(
|
|
701
819
|
neo4j_session: neo4j.Session,
|
|
702
|
-
|
|
703
|
-
policy_name: str,
|
|
704
|
-
statements: Any,
|
|
820
|
+
policy_statements: dict[str, list[dict[str, Any]]],
|
|
705
821
|
aws_update_tag: int,
|
|
706
822
|
) -> None:
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
statement.notaction = statement_data.NotAction,
|
|
716
|
-
statement.resource = statement_data.Resource,
|
|
717
|
-
statement.notresource = statement_data.NotResource,
|
|
718
|
-
statement.condition = statement_data.Condition,
|
|
719
|
-
statement.sid = statement_data.Sid,
|
|
720
|
-
statement.lastupdated = $aws_update_tag
|
|
721
|
-
MERGE (policy)-[r:STATEMENT]->(statement)
|
|
722
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
723
|
-
SET r.lastupdated = $aws_update_tag
|
|
724
|
-
"""
|
|
725
|
-
neo4j_session.run(
|
|
726
|
-
ingest_policy_statement,
|
|
727
|
-
PolicyId=policy_id,
|
|
728
|
-
PolicyName=policy_name,
|
|
729
|
-
Statements=statements,
|
|
730
|
-
aws_update_tag=aws_update_tag,
|
|
731
|
-
).consume()
|
|
823
|
+
for policy_id, statements in policy_statements.items():
|
|
824
|
+
load(
|
|
825
|
+
neo4j_session,
|
|
826
|
+
AWSPolicyStatementSchema(),
|
|
827
|
+
statements,
|
|
828
|
+
lastupdated=aws_update_tag,
|
|
829
|
+
POLICY_ID=policy_id,
|
|
830
|
+
)
|
|
732
831
|
|
|
733
832
|
|
|
734
833
|
@timeit
|
|
735
834
|
def load_policy_data(
|
|
736
835
|
neo4j_session: neo4j.Session,
|
|
737
|
-
|
|
738
|
-
policy_type: str,
|
|
836
|
+
transformed_policy_data: TransformedPolicyData,
|
|
739
837
|
aws_update_tag: int,
|
|
838
|
+
current_aws_account_id: str,
|
|
740
839
|
) -> None:
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
)
|
|
755
|
-
if policy_type == PolicyType.inline.value
|
|
756
|
-
else policy_key
|
|
757
|
-
)
|
|
758
|
-
load_policy(
|
|
759
|
-
neo4j_session,
|
|
760
|
-
policy_id,
|
|
761
|
-
policy_name,
|
|
762
|
-
policy_type,
|
|
763
|
-
principal_arn,
|
|
764
|
-
aws_update_tag,
|
|
765
|
-
)
|
|
766
|
-
load_policy_statements(
|
|
767
|
-
neo4j_session,
|
|
768
|
-
policy_id,
|
|
769
|
-
policy_name,
|
|
770
|
-
statements,
|
|
771
|
-
aws_update_tag,
|
|
772
|
-
)
|
|
840
|
+
_load_policy(
|
|
841
|
+
neo4j_session,
|
|
842
|
+
transformed_policy_data.managed_policies,
|
|
843
|
+
transformed_policy_data.inline_policies,
|
|
844
|
+
current_aws_account_id,
|
|
845
|
+
aws_update_tag,
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
_load_policy_statements(
|
|
849
|
+
neo4j_session,
|
|
850
|
+
transformed_policy_data.statements_by_policy_id,
|
|
851
|
+
aws_update_tag,
|
|
852
|
+
)
|
|
773
853
|
|
|
774
854
|
|
|
775
855
|
@timeit
|
|
776
856
|
def sync_users(
|
|
777
857
|
neo4j_session: neo4j.Session,
|
|
778
|
-
boto3_session: boto3.
|
|
858
|
+
boto3_session: boto3.Session,
|
|
779
859
|
current_aws_account_id: str,
|
|
780
860
|
aws_update_tag: int,
|
|
781
861
|
common_job_parameters: Dict,
|
|
782
862
|
) -> None:
|
|
783
863
|
logger.info("Syncing IAM users for account '%s'.", current_aws_account_id)
|
|
784
864
|
data = get_user_list_data(boto3_session)
|
|
785
|
-
|
|
865
|
+
user_data = transform_users(data["Users"])
|
|
866
|
+
load_users(neo4j_session, user_data, current_aws_account_id, aws_update_tag)
|
|
786
867
|
|
|
787
|
-
sync_user_inline_policies(
|
|
868
|
+
sync_user_inline_policies(
|
|
869
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
870
|
+
)
|
|
788
871
|
|
|
789
|
-
sync_user_managed_policies(
|
|
872
|
+
sync_user_managed_policies(
|
|
873
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
874
|
+
)
|
|
790
875
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
876
|
+
|
|
877
|
+
@timeit
|
|
878
|
+
def sync_user_access_keys(
|
|
879
|
+
neo4j_session: neo4j.Session,
|
|
880
|
+
boto3_session: boto3.Session,
|
|
881
|
+
current_aws_account_id: str,
|
|
882
|
+
aws_update_tag: int,
|
|
883
|
+
common_job_parameters: Dict,
|
|
884
|
+
) -> None:
|
|
885
|
+
logger.info(
|
|
886
|
+
"Syncing IAM user access keys for account '%s'.", current_aws_account_id
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
# Query the graph for users instead of making another AWS API call
|
|
890
|
+
query = (
|
|
891
|
+
"MATCH (user:AWSUser)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ID}) "
|
|
892
|
+
"RETURN user.name as name, user.arn as arn"
|
|
893
|
+
)
|
|
894
|
+
users = neo4j_session.execute_read(
|
|
895
|
+
read_list_of_dicts_tx,
|
|
896
|
+
query,
|
|
897
|
+
AWS_ID=current_aws_account_id,
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
user_access_keys = get_user_access_keys_data(boto3_session, users)
|
|
901
|
+
access_key_data = transform_access_keys(user_access_keys)
|
|
902
|
+
load_access_keys(
|
|
903
|
+
neo4j_session, access_key_data, aws_update_tag, current_aws_account_id
|
|
904
|
+
)
|
|
905
|
+
GraphJob.from_node_schema(AccountAccessKeySchema(), common_job_parameters).run(
|
|
906
|
+
neo4j_session
|
|
795
907
|
)
|
|
796
908
|
|
|
797
909
|
|
|
798
910
|
@timeit
|
|
799
911
|
def sync_user_managed_policies(
|
|
800
|
-
boto3_session: boto3.
|
|
912
|
+
boto3_session: boto3.Session,
|
|
801
913
|
data: Dict,
|
|
802
914
|
neo4j_session: neo4j.Session,
|
|
803
915
|
aws_update_tag: int,
|
|
916
|
+
current_aws_account_id: str,
|
|
804
917
|
) -> None:
|
|
805
918
|
managed_policy_data = get_user_managed_policy_data(boto3_session, data["Users"])
|
|
806
|
-
transform_policy_data(
|
|
919
|
+
transformed_policy_data = transform_policy_data(
|
|
920
|
+
managed_policy_data, PolicyType.managed.value
|
|
921
|
+
)
|
|
807
922
|
load_policy_data(
|
|
808
923
|
neo4j_session,
|
|
809
|
-
|
|
810
|
-
PolicyType.managed.value,
|
|
924
|
+
transformed_policy_data,
|
|
811
925
|
aws_update_tag,
|
|
926
|
+
current_aws_account_id,
|
|
812
927
|
)
|
|
813
928
|
|
|
814
929
|
|
|
815
930
|
@timeit
|
|
816
931
|
def sync_user_inline_policies(
|
|
817
|
-
boto3_session: boto3.
|
|
932
|
+
boto3_session: boto3.Session,
|
|
818
933
|
data: Dict,
|
|
819
934
|
neo4j_session: neo4j.Session,
|
|
820
935
|
aws_update_tag: int,
|
|
936
|
+
current_aws_account_id: str,
|
|
821
937
|
) -> None:
|
|
822
938
|
policy_data = get_user_policy_data(boto3_session, data["Users"])
|
|
823
|
-
transform_policy_data(
|
|
939
|
+
transformed_policy_data = transform_policy_data(
|
|
940
|
+
policy_data, PolicyType.inline.value
|
|
941
|
+
)
|
|
824
942
|
load_policy_data(
|
|
825
943
|
neo4j_session,
|
|
826
|
-
|
|
827
|
-
PolicyType.inline.value,
|
|
944
|
+
transformed_policy_data,
|
|
828
945
|
aws_update_tag,
|
|
946
|
+
current_aws_account_id,
|
|
829
947
|
)
|
|
830
948
|
|
|
831
949
|
|
|
832
950
|
@timeit
|
|
833
951
|
def sync_groups(
|
|
834
952
|
neo4j_session: neo4j.Session,
|
|
835
|
-
boto3_session: boto3.
|
|
953
|
+
boto3_session: boto3.Session,
|
|
836
954
|
current_aws_account_id: str,
|
|
837
955
|
aws_update_tag: int,
|
|
838
956
|
common_job_parameters: Dict,
|
|
839
957
|
) -> None:
|
|
840
958
|
logger.info("Syncing IAM groups for account '%s'.", current_aws_account_id)
|
|
841
959
|
data = get_group_list_data(boto3_session)
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
960
|
+
group_memberships = get_group_memberships(boto3_session, data["Groups"])
|
|
961
|
+
group_data = transform_groups(data["Groups"], group_memberships)
|
|
962
|
+
load_groups(neo4j_session, group_data, current_aws_account_id, aws_update_tag)
|
|
845
963
|
|
|
846
|
-
|
|
964
|
+
sync_groups_inline_policies(
|
|
965
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
966
|
+
)
|
|
847
967
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
neo4j_session,
|
|
851
|
-
common_job_parameters,
|
|
968
|
+
sync_group_managed_policies(
|
|
969
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
852
970
|
)
|
|
853
971
|
|
|
854
972
|
|
|
855
973
|
def sync_group_managed_policies(
|
|
856
|
-
boto3_session: boto3.
|
|
974
|
+
boto3_session: boto3.Session,
|
|
857
975
|
data: Dict,
|
|
858
976
|
neo4j_session: neo4j.Session,
|
|
859
977
|
aws_update_tag: int,
|
|
978
|
+
current_aws_account_id: str,
|
|
860
979
|
) -> None:
|
|
861
980
|
managed_policy_data = get_group_managed_policy_data(boto3_session, data["Groups"])
|
|
862
|
-
transform_policy_data(
|
|
981
|
+
transformed_policy_data = transform_policy_data(
|
|
982
|
+
managed_policy_data, PolicyType.managed.value
|
|
983
|
+
)
|
|
863
984
|
load_policy_data(
|
|
864
985
|
neo4j_session,
|
|
865
|
-
|
|
866
|
-
PolicyType.managed.value,
|
|
986
|
+
transformed_policy_data,
|
|
867
987
|
aws_update_tag,
|
|
988
|
+
current_aws_account_id,
|
|
868
989
|
)
|
|
869
990
|
|
|
870
991
|
|
|
871
992
|
def sync_groups_inline_policies(
|
|
872
|
-
boto3_session: boto3.
|
|
993
|
+
boto3_session: boto3.Session,
|
|
873
994
|
data: Dict,
|
|
874
995
|
neo4j_session: neo4j.Session,
|
|
875
996
|
aws_update_tag: int,
|
|
997
|
+
current_aws_account_id: str,
|
|
876
998
|
) -> None:
|
|
877
999
|
policy_data = get_group_policy_data(boto3_session, data["Groups"])
|
|
878
|
-
transform_policy_data(
|
|
1000
|
+
transformed_policy_data = transform_policy_data(
|
|
1001
|
+
policy_data, PolicyType.inline.value
|
|
1002
|
+
)
|
|
879
1003
|
load_policy_data(
|
|
880
1004
|
neo4j_session,
|
|
881
|
-
|
|
882
|
-
|
|
1005
|
+
transformed_policy_data,
|
|
1006
|
+
aws_update_tag,
|
|
1007
|
+
current_aws_account_id,
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def load_external_aws_accounts(
|
|
1012
|
+
neo4j_session: neo4j.Session,
|
|
1013
|
+
external_aws_accounts: list[dict[str, Any]],
|
|
1014
|
+
aws_update_tag: int,
|
|
1015
|
+
) -> None:
|
|
1016
|
+
load(
|
|
1017
|
+
neo4j_session,
|
|
1018
|
+
AWSAccountAWSRoleSchema(),
|
|
1019
|
+
external_aws_accounts,
|
|
1020
|
+
lastupdated=aws_update_tag,
|
|
1021
|
+
)
|
|
1022
|
+
# Ensure that the root principal exists for each external account.
|
|
1023
|
+
for account in external_aws_accounts:
|
|
1024
|
+
sync_root_principal(
|
|
1025
|
+
neo4j_session,
|
|
1026
|
+
account["id"],
|
|
1027
|
+
aws_update_tag,
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
@timeit
|
|
1032
|
+
def load_service_principals(
|
|
1033
|
+
neo4j_session: neo4j.Session,
|
|
1034
|
+
service_principals: list[dict[str, Any]],
|
|
1035
|
+
aws_update_tag: int,
|
|
1036
|
+
) -> None:
|
|
1037
|
+
load(
|
|
1038
|
+
neo4j_session,
|
|
1039
|
+
AWSServicePrincipalSchema(),
|
|
1040
|
+
service_principals,
|
|
1041
|
+
lastupdated=aws_update_tag,
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
@timeit
|
|
1046
|
+
def load_role_data(
|
|
1047
|
+
neo4j_session: neo4j.Session,
|
|
1048
|
+
role_list: list[dict[str, Any]],
|
|
1049
|
+
current_aws_account_id: str,
|
|
1050
|
+
aws_update_tag: int,
|
|
1051
|
+
) -> None:
|
|
1052
|
+
# Note that the account_id is set in the transform_roles function instead of from the `AWS_ID` kwarg like in other modules
|
|
1053
|
+
# because this can create root principals from other accounts based on data from the assume role policy document.
|
|
1054
|
+
load(
|
|
1055
|
+
neo4j_session,
|
|
1056
|
+
AWSRoleSchema(),
|
|
1057
|
+
role_list,
|
|
1058
|
+
lastupdated=aws_update_tag,
|
|
1059
|
+
AWS_ID=current_aws_account_id,
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
@timeit
|
|
1064
|
+
def load_federated_principals(
|
|
1065
|
+
neo4j_session: neo4j.Session,
|
|
1066
|
+
federated_principals: list[dict[str, Any]],
|
|
1067
|
+
current_aws_account_id: str,
|
|
1068
|
+
aws_update_tag: int,
|
|
1069
|
+
) -> None:
|
|
1070
|
+
load(
|
|
1071
|
+
neo4j_session,
|
|
1072
|
+
AWSFederatedPrincipalSchema(),
|
|
1073
|
+
federated_principals,
|
|
1074
|
+
lastupdated=aws_update_tag,
|
|
1075
|
+
AWS_ID=current_aws_account_id,
|
|
1076
|
+
)
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
@timeit
|
|
1080
|
+
def sync_role_assumptions(
|
|
1081
|
+
neo4j_session: neo4j.Session,
|
|
1082
|
+
data: dict[str, Any],
|
|
1083
|
+
current_aws_account_id: str,
|
|
1084
|
+
aws_update_tag: int,
|
|
1085
|
+
) -> None:
|
|
1086
|
+
transformed = transform_role_trust_policies(data["Roles"], current_aws_account_id)
|
|
1087
|
+
|
|
1088
|
+
# Order matters here.
|
|
1089
|
+
# External accounts come first because they need to be created before the roles that trust them.
|
|
1090
|
+
load_external_aws_accounts(
|
|
1091
|
+
neo4j_session, transformed.external_aws_accounts, aws_update_tag
|
|
1092
|
+
)
|
|
1093
|
+
# Service principals e.g. arn = "ec2.amazonaws.com" come next because they're global
|
|
1094
|
+
load_service_principals(
|
|
1095
|
+
neo4j_session, transformed.service_principals, aws_update_tag
|
|
1096
|
+
)
|
|
1097
|
+
load_federated_principals(
|
|
1098
|
+
neo4j_session,
|
|
1099
|
+
transformed.federated_principals,
|
|
1100
|
+
current_aws_account_id,
|
|
883
1101
|
aws_update_tag,
|
|
884
1102
|
)
|
|
1103
|
+
load_role_data(
|
|
1104
|
+
neo4j_session, transformed.role_data, current_aws_account_id, aws_update_tag
|
|
1105
|
+
)
|
|
885
1106
|
|
|
886
1107
|
|
|
887
1108
|
@timeit
|
|
888
1109
|
def sync_roles(
|
|
889
1110
|
neo4j_session: neo4j.Session,
|
|
890
|
-
boto3_session: boto3.
|
|
1111
|
+
boto3_session: boto3.Session,
|
|
891
1112
|
current_aws_account_id: str,
|
|
892
1113
|
aws_update_tag: int,
|
|
893
1114
|
common_job_parameters: Dict,
|
|
894
1115
|
) -> None:
|
|
895
1116
|
logger.info("Syncing IAM roles for account '%s'.", current_aws_account_id)
|
|
896
1117
|
data = get_role_list_data(boto3_session)
|
|
897
|
-
|
|
1118
|
+
|
|
1119
|
+
sync_role_assumptions(neo4j_session, data, current_aws_account_id, aws_update_tag)
|
|
898
1120
|
|
|
899
1121
|
sync_role_inline_policies(
|
|
900
1122
|
current_aws_account_id,
|
|
@@ -912,16 +1134,10 @@ def sync_roles(
|
|
|
912
1134
|
aws_update_tag,
|
|
913
1135
|
)
|
|
914
1136
|
|
|
915
|
-
run_cleanup_job(
|
|
916
|
-
"aws_import_roles_cleanup.json",
|
|
917
|
-
neo4j_session,
|
|
918
|
-
common_job_parameters,
|
|
919
|
-
)
|
|
920
|
-
|
|
921
1137
|
|
|
922
1138
|
def sync_role_managed_policies(
|
|
923
1139
|
current_aws_account_id: str,
|
|
924
|
-
boto3_session: boto3.
|
|
1140
|
+
boto3_session: boto3.Session,
|
|
925
1141
|
data: Dict,
|
|
926
1142
|
neo4j_session: neo4j.Session,
|
|
927
1143
|
aws_update_tag: int,
|
|
@@ -931,18 +1147,20 @@ def sync_role_managed_policies(
|
|
|
931
1147
|
current_aws_account_id,
|
|
932
1148
|
)
|
|
933
1149
|
managed_policy_data = get_role_managed_policy_data(boto3_session, data["Roles"])
|
|
934
|
-
transform_policy_data(
|
|
1150
|
+
transformed_policy_data = transform_policy_data(
|
|
1151
|
+
managed_policy_data, PolicyType.managed.value
|
|
1152
|
+
)
|
|
935
1153
|
load_policy_data(
|
|
936
1154
|
neo4j_session,
|
|
937
|
-
|
|
938
|
-
PolicyType.managed.value,
|
|
1155
|
+
transformed_policy_data,
|
|
939
1156
|
aws_update_tag,
|
|
1157
|
+
current_aws_account_id,
|
|
940
1158
|
)
|
|
941
1159
|
|
|
942
1160
|
|
|
943
1161
|
def sync_role_inline_policies(
|
|
944
1162
|
current_aws_account_id: str,
|
|
945
|
-
boto3_session: boto3.
|
|
1163
|
+
boto3_session: boto3.Session,
|
|
946
1164
|
data: Dict,
|
|
947
1165
|
neo4j_session: neo4j.Session,
|
|
948
1166
|
aws_update_tag: int,
|
|
@@ -952,76 +1170,121 @@ def sync_role_inline_policies(
|
|
|
952
1170
|
current_aws_account_id,
|
|
953
1171
|
)
|
|
954
1172
|
inline_policy_data = get_role_policy_data(boto3_session, data["Roles"])
|
|
955
|
-
transform_policy_data(
|
|
1173
|
+
transformed_policy_data = transform_policy_data(
|
|
1174
|
+
inline_policy_data, PolicyType.inline.value
|
|
1175
|
+
)
|
|
956
1176
|
load_policy_data(
|
|
957
1177
|
neo4j_session,
|
|
958
|
-
|
|
959
|
-
PolicyType.inline.value,
|
|
1178
|
+
transformed_policy_data,
|
|
960
1179
|
aws_update_tag,
|
|
1180
|
+
current_aws_account_id,
|
|
961
1181
|
)
|
|
962
1182
|
|
|
963
1183
|
|
|
1184
|
+
def _get_policies_in_current_account(
|
|
1185
|
+
neo4j_session: neo4j.Session, current_aws_account_id: str
|
|
1186
|
+
) -> list[str]:
|
|
1187
|
+
query = """
|
|
1188
|
+
MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(p:AWSPolicy)
|
|
1189
|
+
RETURN p.id
|
|
1190
|
+
"""
|
|
1191
|
+
return [
|
|
1192
|
+
str(policy_id)
|
|
1193
|
+
for policy_id in neo4j_session.execute_read(
|
|
1194
|
+
read_list_of_values_tx,
|
|
1195
|
+
query,
|
|
1196
|
+
AWS_ID=current_aws_account_id,
|
|
1197
|
+
)
|
|
1198
|
+
]
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
def _get_principals_with_pols_in_current_account(
|
|
1202
|
+
neo4j_session: neo4j.Session, current_aws_account_id: str
|
|
1203
|
+
) -> list[str]:
|
|
1204
|
+
query = """
|
|
1205
|
+
MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(p:AWSPrincipal)
|
|
1206
|
+
WHERE (p)-[:POLICY]->(:AWSPolicy)
|
|
1207
|
+
RETURN p.id
|
|
1208
|
+
"""
|
|
1209
|
+
return [
|
|
1210
|
+
str(principal_id)
|
|
1211
|
+
for principal_id in neo4j_session.execute_read(
|
|
1212
|
+
read_list_of_values_tx,
|
|
1213
|
+
query,
|
|
1214
|
+
AWS_ID=current_aws_account_id,
|
|
1215
|
+
)
|
|
1216
|
+
]
|
|
1217
|
+
|
|
1218
|
+
|
|
964
1219
|
@timeit
|
|
965
|
-
def
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
aws_update_tag: int,
|
|
970
|
-
common_job_parameters: Dict,
|
|
971
|
-
) -> None:
|
|
972
|
-
logger.info(
|
|
973
|
-
"Syncing IAM group membership for account '%s'.",
|
|
974
|
-
current_aws_account_id,
|
|
1220
|
+
def cleanup_iam(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
|
|
1221
|
+
# List all policies in the current account
|
|
1222
|
+
policy_ids = _get_policies_in_current_account(
|
|
1223
|
+
neo4j_session, common_job_parameters["AWS_ID"]
|
|
975
1224
|
)
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1225
|
+
|
|
1226
|
+
# for each policy id, run the cleanup job for the policy statements, passing the policy id as a kwarg.
|
|
1227
|
+
for policy_id in policy_ids:
|
|
1228
|
+
GraphJob.from_node_schema(
|
|
1229
|
+
AWSPolicyStatementSchema(),
|
|
1230
|
+
{**common_job_parameters, "POLICY_ID": policy_id},
|
|
1231
|
+
).run(
|
|
1232
|
+
neo4j_session,
|
|
1233
|
+
)
|
|
1234
|
+
|
|
1235
|
+
# Next, clean up the policies
|
|
1236
|
+
# Note that managed policies don't have a sub resource relationship. This means that we will only clean up
|
|
1237
|
+
# stale relationships and not stale AWSManagedPolicy nodes. This is because AWSManagedPolicy nodes are global
|
|
1238
|
+
# to AWS and it is possible for them to be shared across accounts, so if we cleaned up an AWSManagedPolicy node
|
|
1239
|
+
# for one account, it would be erroneously deleted for all accounts. Instead, we just clean up the relationships.
|
|
1240
|
+
GraphJob.from_node_schema(AWSManagedPolicySchema(), common_job_parameters).run(
|
|
1241
|
+
neo4j_session
|
|
990
1242
|
)
|
|
991
1243
|
|
|
1244
|
+
# Inline policies are simpler in that they are scoped to a single principal and therefore attached to that
|
|
1245
|
+
# principal's account. This means that this operation will clean up stale AWSInlinePolicy nodes.
|
|
1246
|
+
GraphJob.from_node_schema(AWSInlinePolicySchema(), common_job_parameters).run(
|
|
1247
|
+
neo4j_session
|
|
1248
|
+
)
|
|
992
1249
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
current_aws_account_id: str,
|
|
998
|
-
aws_update_tag: int,
|
|
999
|
-
common_job_parameters: Dict,
|
|
1000
|
-
) -> None:
|
|
1001
|
-
logger.info(
|
|
1002
|
-
"Syncing IAM user access keys for account '%s'.",
|
|
1003
|
-
current_aws_account_id,
|
|
1250
|
+
# Clean up roles before federated and service principals
|
|
1251
|
+
GraphJob.from_node_schema(AWSRoleSchema(), common_job_parameters).run(neo4j_session)
|
|
1252
|
+
GraphJob.from_node_schema(AWSFederatedPrincipalSchema(), common_job_parameters).run(
|
|
1253
|
+
neo4j_session
|
|
1004
1254
|
)
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1255
|
+
GraphJob.from_node_schema(AWSServicePrincipalSchema(), common_job_parameters).run(
|
|
1256
|
+
neo4j_session
|
|
1257
|
+
)
|
|
1258
|
+
GraphJob.from_node_schema(AWSUserSchema(), common_job_parameters).run(neo4j_session)
|
|
1259
|
+
GraphJob.from_node_schema(AWSGroupSchema(), common_job_parameters).run(
|
|
1260
|
+
neo4j_session
|
|
1008
1261
|
)
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
def sync_root_principal(
|
|
1265
|
+
neo4j_session: neo4j.Session, current_aws_account_id: str, aws_update_tag: int
|
|
1266
|
+
) -> None:
|
|
1267
|
+
"""
|
|
1268
|
+
In the current account, create a node for the AWS root principal "arn:aws:iam::<account_id>:root".
|
|
1269
|
+
|
|
1270
|
+
If a role X trusts the root principal in an account A, then any other role Y in A can assume X.
|
|
1271
|
+
|
|
1272
|
+
Note that this is _not_ the same as the AWS root user. The root principal doesn't show up in any
|
|
1273
|
+
APIs except for assumerole trust policies.
|
|
1274
|
+
"""
|
|
1275
|
+
load(
|
|
1016
1276
|
neo4j_session,
|
|
1017
|
-
|
|
1277
|
+
AWSRootPrincipalSchema(),
|
|
1278
|
+
[{"arn": f"arn:aws:iam::{current_aws_account_id}:root"}],
|
|
1279
|
+
lastupdated=aws_update_tag,
|
|
1280
|
+
AWS_ID=current_aws_account_id,
|
|
1018
1281
|
)
|
|
1019
1282
|
|
|
1020
1283
|
|
|
1021
1284
|
@timeit
|
|
1022
1285
|
def sync(
|
|
1023
1286
|
neo4j_session: neo4j.Session,
|
|
1024
|
-
boto3_session: boto3.
|
|
1287
|
+
boto3_session: boto3.Session,
|
|
1025
1288
|
regions: List[str],
|
|
1026
1289
|
current_aws_account_id: str,
|
|
1027
1290
|
update_tag: int,
|
|
@@ -1030,28 +1293,26 @@ def sync(
|
|
|
1030
1293
|
logger.info("Syncing IAM for account '%s'.", current_aws_account_id)
|
|
1031
1294
|
# This module only syncs IAM information that is in use.
|
|
1032
1295
|
# As such only policies that are attached to a user, role or group are synced
|
|
1033
|
-
|
|
1296
|
+
sync_root_principal(
|
|
1034
1297
|
neo4j_session,
|
|
1035
|
-
boto3_session,
|
|
1036
1298
|
current_aws_account_id,
|
|
1037
1299
|
update_tag,
|
|
1038
|
-
common_job_parameters,
|
|
1039
1300
|
)
|
|
1040
|
-
|
|
1301
|
+
sync_users(
|
|
1041
1302
|
neo4j_session,
|
|
1042
1303
|
boto3_session,
|
|
1043
1304
|
current_aws_account_id,
|
|
1044
1305
|
update_tag,
|
|
1045
1306
|
common_job_parameters,
|
|
1046
1307
|
)
|
|
1047
|
-
|
|
1308
|
+
sync_groups(
|
|
1048
1309
|
neo4j_session,
|
|
1049
1310
|
boto3_session,
|
|
1050
1311
|
current_aws_account_id,
|
|
1051
1312
|
update_tag,
|
|
1052
1313
|
common_job_parameters,
|
|
1053
1314
|
)
|
|
1054
|
-
|
|
1315
|
+
sync_roles(
|
|
1055
1316
|
neo4j_session,
|
|
1056
1317
|
boto3_session,
|
|
1057
1318
|
current_aws_account_id,
|
|
@@ -1071,11 +1332,7 @@ def sync(
|
|
|
1071
1332
|
update_tag,
|
|
1072
1333
|
common_job_parameters,
|
|
1073
1334
|
)
|
|
1074
|
-
|
|
1075
|
-
"aws_import_principals_cleanup.json",
|
|
1076
|
-
neo4j_session,
|
|
1077
|
-
common_job_parameters,
|
|
1078
|
-
)
|
|
1335
|
+
cleanup_iam(neo4j_session, common_job_parameters)
|
|
1079
1336
|
merge_module_sync_metadata(
|
|
1080
1337
|
neo4j_session,
|
|
1081
1338
|
group_type="AWSAccount",
|