cartography 0.93.0rc1__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/__main__.py +1 -2
- cartography/_version.py +34 -0
- cartography/cli.py +903 -225
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +400 -27
- cartography/config.py +215 -10
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +1 -200
- cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -5
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +27 -0
- cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
- cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +13 -13
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +72 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +255 -35
- cartography/graph/job.py +104 -20
- cartography/graph/querybuilder.py +689 -91
- cartography/graph/statement.py +49 -36
- 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/analysis.py +4 -1
- cartography/intel/anthropic/__init__.py +62 -0
- cartography/intel/anthropic/apikeys.py +72 -0
- cartography/intel/anthropic/users.py +75 -0
- cartography/intel/anthropic/util.py +51 -0
- cartography/intel/anthropic/workspaces.py +95 -0
- cartography/intel/aws/__init__.py +137 -59
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +482 -217
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +105 -0
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +239 -0
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +63 -23
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +254 -189
- cartography/intel/aws/ec2/elastic_ip_addresses.py +44 -14
- cartography/intel/aws/ec2/images.py +74 -39
- cartography/intel/aws/ec2/instances.py +262 -137
- cartography/intel/aws/ec2/internet_gateways.py +44 -13
- cartography/intel/aws/ec2/key_pairs.py +72 -39
- cartography/intel/aws/ec2/launch_templates.py +143 -66
- cartography/intel/aws/ec2/load_balancer_v2s.py +119 -45
- cartography/intel/aws/ec2/load_balancers.py +165 -147
- cartography/intel/aws/ec2/network_acls.py +233 -0
- cartography/intel/aws/ec2/network_interfaces.py +150 -87
- cartography/intel/aws/ec2/reserved_instances.py +48 -17
- cartography/intel/aws/ec2/route_tables.py +327 -0
- cartography/intel/aws/ec2/security_groups.py +189 -121
- cartography/intel/aws/ec2/snapshots.py +93 -91
- cartography/intel/aws/ec2/subnets.py +70 -58
- cartography/intel/aws/ec2/tgw.py +111 -39
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +157 -116
- cartography/intel/aws/ec2/vpc_peerings.py +317 -121
- cartography/intel/aws/ecr.py +336 -93
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +310 -403
- cartography/intel/aws/efs.py +261 -0
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +130 -83
- cartography/intel/aws/elasticsearch.py +70 -24
- cartography/intel/aws/emr.py +61 -23
- 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 +978 -464
- cartography/intel/aws/iam_instance_profiles.py +73 -0
- cartography/intel/aws/identitycenter.py +847 -0
- cartography/intel/aws/inspector.py +330 -133
- cartography/intel/aws/kms.py +235 -209
- cartography/intel/aws/lambda_function.py +328 -176
- cartography/intel/aws/organizations.py +40 -19
- cartography/intel/aws/permission_relationships.py +144 -68
- cartography/intel/aws/rds.py +467 -412
- cartography/intel/aws/redshift.py +116 -50
- cartography/intel/aws/resourcegroupstaggingapi.py +198 -82
- cartography/intel/aws/resources.py +80 -42
- cartography/intel/aws/route53.py +419 -318
- cartography/intel/aws/s3.py +489 -96
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +217 -40
- cartography/intel/aws/securityhub.py +23 -10
- cartography/intel/aws/sns.py +226 -0
- cartography/intel/aws/sqs.py +74 -96
- cartography/intel/aws/ssm.py +142 -33
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +259 -46
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +141 -120
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +706 -519
- 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 +436 -392
- cartography/intel/azure/storage.py +467 -335
- cartography/intel/azure/subscription.py +49 -55
- cartography/intel/azure/tenant.py +46 -28
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +58 -143
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/cloudflare/__init__.py +74 -0
- cartography/intel/cloudflare/accounts.py +57 -0
- cartography/intel/cloudflare/dnsrecords.py +64 -0
- cartography/intel/cloudflare/members.py +75 -0
- cartography/intel/cloudflare/roles.py +65 -0
- cartography/intel/cloudflare/zones.py +64 -0
- cartography/intel/create_indexes.py +5 -3
- cartography/intel/crowdstrike/__init__.py +26 -12
- cartography/intel/crowdstrike/endpoints.py +17 -45
- cartography/intel/crowdstrike/spotlight.py +13 -5
- cartography/intel/cve/__init__.py +91 -26
- cartography/intel/cve/feed.py +77 -56
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +41 -12
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +160 -0
- 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 +136 -0
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +259 -0
- cartography/intel/gcp/__init__.py +381 -175
- 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 +521 -325
- 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 +134 -179
- cartography/intel/gcp/gke.py +100 -107
- cartography/intel/gcp/iam.py +262 -0
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +103 -158
- cartography/intel/github/__init__.py +66 -27
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +871 -160
- cartography/intel/github/teams.py +386 -53
- cartography/intel/github/users.py +214 -49
- cartography/intel/github/util.py +50 -35
- 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 +101 -42
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +37 -8
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +40 -10
- 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 +60 -55
- cartography/intel/kubernetes/pods.py +171 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -63
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +157 -47
- cartography/intel/oci/organizations.py +16 -7
- cartography/intel/oci/utils.py +71 -25
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +57 -25
- cartography/intel/okta/awssaml.py +105 -41
- cartography/intel/okta/factors.py +19 -5
- cartography/intel/okta/groups.py +61 -31
- cartography/intel/okta/organization.py +8 -2
- cartography/intel/okta/origins.py +9 -3
- cartography/intel/okta/roles.py +20 -7
- cartography/intel/okta/users.py +31 -10
- cartography/intel/okta/utils.py +6 -4
- 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/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +89 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +97 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +75 -0
- cartography/intel/openai/util.py +45 -0
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +31 -12
- cartography/intel/pagerduty/schedules.py +21 -8
- cartography/intel/pagerduty/services.py +18 -7
- cartography/intel/pagerduty/teams.py +13 -5
- cartography/intel/pagerduty/users.py +6 -2
- cartography/intel/pagerduty/vendors.py +6 -2
- 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/semgrep/__init__.py +30 -5
- cartography/intel/semgrep/dependencies.py +255 -0
- cartography/intel/semgrep/deployment.py +69 -0
- cartography/intel/semgrep/findings.py +157 -117
- 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/snipeit/__init__.py +44 -0
- cartography/intel/snipeit/asset.py +80 -0
- cartography/intel/snipeit/user.py +78 -0
- cartography/intel/snipeit/util.py +40 -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/tailscale/__init__.py +77 -0
- cartography/intel/tailscale/acls.py +146 -0
- cartography/intel/tailscale/devices.py +127 -0
- cartography/intel/tailscale/postureintegrations.py +81 -0
- cartography/intel/tailscale/tailnets.py +76 -0
- cartography/intel/tailscale/users.py +80 -0
- cartography/intel/tailscale/utils.py +132 -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/__init__.py +0 -0
- cartography/models/anthropic/apikey.py +94 -0
- cartography/models/anthropic/organization.py +19 -0
- cartography/models/anthropic/user.py +52 -0
- cartography/models/anthropic/workspace.py +90 -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/apigateway.py +51 -0
- cartography/models/aws/apigateway/apigatewaycertificate.py +72 -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/apigateway/apigatewayresource.py +70 -0
- cartography/models/aws/apigateway/apigatewaystage.py +75 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +106 -0
- cartography/models/aws/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -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/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +27 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +224 -0
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +85 -38
- cartography/models/aws/ec2/keypair.py +59 -0
- cartography/models/aws/ec2/keypair_instance.py +76 -0
- cartography/models/aws/ec2/launch_configurations.py +59 -0
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +72 -0
- cartography/models/aws/ec2/load_balancers.py +112 -0
- cartography/models/aws/ec2/network_acl_rules.py +106 -0
- cartography/models/aws/ec2/network_acls.py +95 -0
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +57 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +26 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +42 -31
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +67 -40
- 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/__init__.py +0 -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 +79 -0
- cartography/models/aws/eks/clusters.py +23 -21
- 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/emr.py +32 -30
- 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/__init__.py +0 -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/instanceprofile.py +76 -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/__init__.py +0 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +49 -0
- cartography/models/aws/identitycenter/awspermissionset.py +162 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +110 -0
- cartography/models/aws/inspector/findings.py +124 -58
- cartography/models/aws/inspector/packages.py +18 -42
- 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/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/__init__.py +0 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +114 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- 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/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/aws/ssm/parameters.py +84 -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 +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/cloudflare/__init__.py +0 -0
- cartography/models/cloudflare/account.py +25 -0
- cartography/models/cloudflare/dnsrecord.py +55 -0
- cartography/models/cloudflare/member.py +86 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- cartography/models/core/common.py +53 -2
- cartography/models/core/nodes.py +20 -4
- cartography/models/core/relationships.py +58 -6
- cartography/models/crowdstrike/__init__.py +0 -0
- cartography/models/crowdstrike/hosts.py +51 -0
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +58 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +50 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/__init__.py +0 -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/ou.py +48 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/tenant.py +39 -0
- cartography/models/entra/user.py +90 -0
- 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 +73 -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/orgs.py +27 -0
- cartography/models/github/teams.py +74 -22
- cartography/models/github/users.py +149 -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 +22 -17
- cartography/models/kandji/tenant.py +6 -4
- 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/tenant.py +3 -3
- cartography/models/lastpass/user.py +36 -28
- 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/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +94 -0
- cartography/models/openai/apikey.py +88 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +89 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +53 -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/semgrep/dependencies.py +102 -0
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -40
- cartography/models/semgrep/locations.py +27 -21
- 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/__init__.py +0 -0
- cartography/models/snipeit/asset.py +92 -0
- cartography/models/snipeit/tenant.py +19 -0
- cartography/models/snipeit/user.py +60 -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/__init__.py +0 -0
- cartography/models/tailscale/device.py +96 -0
- cartography/models/tailscale/group.py +86 -0
- cartography/models/tailscale/postureintegration.py +58 -0
- cartography/models/tailscale/tag.py +102 -0
- cartography/models/tailscale/tailnet.py +29 -0
- cartography/models/tailscale/user.py +57 -0
- 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/stats.py +4 -4
- cartography/sync.py +137 -31
- cartography/util.py +187 -77
- cartography-0.123.0.dist-info/METADATA +230 -0
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/WHEEL +1 -1
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info/licenses}/LICENSE +1 -1
- cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
- cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
- cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -10
- 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_apigateway_cleanup.json +0 -45
- 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_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/crxcavator_import_cleanup.json +0 -18
- 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/github_users_cleanup.json +0 -23
- 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/crxcavator/__init__.py +0 -44
- cartography/intel/crxcavator/crxcavator.py +0 -329
- cartography/intel/gcp/crm.py +0 -302
- cartography/intel/gsuite/api.py +0 -284
- cartography/models/aws/ec2/keypairs.py +0 -64
- cartography-0.93.0rc1.dist-info/METADATA +0 -55
- cartography-0.93.0rc1.dist-info/NOTICE +0 -4
- cartography-0.93.0rc1.dist-info/RECORD +0 -341
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- {cartography-0.93.0rc1.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__)
|
|
@@ -24,8 +41,28 @@ stat_handler = get_stats_client(__name__)
|
|
|
24
41
|
|
|
25
42
|
|
|
26
43
|
class PolicyType(enum.Enum):
|
|
27
|
-
managed =
|
|
28
|
-
inline =
|
|
44
|
+
managed = "managed"
|
|
45
|
+
inline = "inline"
|
|
46
|
+
|
|
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
|
+
)
|
|
29
66
|
|
|
30
67
|
|
|
31
68
|
def get_policy_name_from_arn(arn: str) -> str:
|
|
@@ -33,50 +70,69 @@ def get_policy_name_from_arn(arn: str) -> str:
|
|
|
33
70
|
|
|
34
71
|
|
|
35
72
|
@timeit
|
|
36
|
-
def get_group_policies(boto3_session: boto3.
|
|
37
|
-
client = boto3_session.client(
|
|
38
|
-
paginator = client.get_paginator(
|
|
73
|
+
def get_group_policies(boto3_session: boto3.Session, group_name: str) -> Dict:
|
|
74
|
+
client = boto3_session.client("iam")
|
|
75
|
+
paginator = client.get_paginator("list_group_policies")
|
|
39
76
|
policy_names: List[Dict] = []
|
|
40
77
|
for page in paginator.paginate(GroupName=group_name):
|
|
41
|
-
policy_names.extend(page[
|
|
42
|
-
return {
|
|
78
|
+
policy_names.extend(page["PolicyNames"])
|
|
79
|
+
return {"PolicyNames": policy_names}
|
|
43
80
|
|
|
44
81
|
|
|
45
82
|
@timeit
|
|
46
83
|
def get_group_policy_info(
|
|
47
|
-
|
|
84
|
+
boto3_session: boto3.Session,
|
|
85
|
+
group_name: str,
|
|
86
|
+
policy_name: str,
|
|
48
87
|
) -> Any:
|
|
49
|
-
client = boto3_session.client(
|
|
88
|
+
client = boto3_session.client("iam")
|
|
50
89
|
return client.get_group_policy(GroupName=group_name, PolicyName=policy_name)
|
|
51
90
|
|
|
52
91
|
|
|
53
92
|
@timeit
|
|
54
|
-
def get_group_membership_data(
|
|
55
|
-
|
|
93
|
+
def get_group_membership_data(
|
|
94
|
+
boto3_session: boto3.Session,
|
|
95
|
+
group_name: str,
|
|
96
|
+
) -> Dict:
|
|
97
|
+
client = boto3_session.client("iam")
|
|
56
98
|
try:
|
|
57
99
|
memberships = client.get_group(GroupName=group_name)
|
|
58
100
|
return memberships
|
|
59
101
|
except client.exceptions.NoSuchEntityException:
|
|
60
102
|
# Avoid crashing the sync
|
|
61
|
-
logger.warning(
|
|
103
|
+
logger.warning(
|
|
104
|
+
"client.get_group(GroupName='%s') failed with NoSuchEntityException; skipping.",
|
|
105
|
+
group_name,
|
|
106
|
+
)
|
|
62
107
|
return {}
|
|
63
108
|
|
|
64
109
|
|
|
65
110
|
@timeit
|
|
66
|
-
|
|
67
|
-
|
|
111
|
+
@aws_handle_regions
|
|
112
|
+
def get_group_policy_data(
|
|
113
|
+
boto3_session: boto3.Session,
|
|
114
|
+
group_list: List[Dict],
|
|
115
|
+
) -> Dict:
|
|
116
|
+
resource_client = boto3_session.resource("iam")
|
|
68
117
|
policies = {}
|
|
69
118
|
for group in group_list:
|
|
70
119
|
name = group["GroupName"]
|
|
71
120
|
arn = group["Arn"]
|
|
72
121
|
resource_group = resource_client.Group(name)
|
|
73
|
-
policies[arn] = policies[arn] = {
|
|
122
|
+
policies[arn] = policies[arn] = {
|
|
123
|
+
p.name: p.policy_document["Statement"]
|
|
124
|
+
for p in resource_group.policies.all()
|
|
125
|
+
}
|
|
74
126
|
return policies
|
|
75
127
|
|
|
76
128
|
|
|
77
129
|
@timeit
|
|
78
|
-
|
|
79
|
-
|
|
130
|
+
@aws_handle_regions
|
|
131
|
+
def get_group_managed_policy_data(
|
|
132
|
+
boto3_session: boto3.Session,
|
|
133
|
+
group_list: List[Dict],
|
|
134
|
+
) -> Dict:
|
|
135
|
+
resource_client = boto3_session.resource("iam")
|
|
80
136
|
policies = {}
|
|
81
137
|
for group in group_list:
|
|
82
138
|
name = group["GroupName"]
|
|
@@ -90,15 +146,22 @@ def get_group_managed_policy_data(boto3_session: boto3.session.Session, group_li
|
|
|
90
146
|
|
|
91
147
|
|
|
92
148
|
@timeit
|
|
93
|
-
|
|
94
|
-
|
|
149
|
+
@aws_handle_regions
|
|
150
|
+
def get_user_policy_data(
|
|
151
|
+
boto3_session: boto3.Session,
|
|
152
|
+
user_list: List[Dict],
|
|
153
|
+
) -> Dict:
|
|
154
|
+
resource_client = boto3_session.resource("iam")
|
|
95
155
|
policies = {}
|
|
96
156
|
for user in user_list:
|
|
97
157
|
name = user["UserName"]
|
|
98
158
|
arn = user["Arn"]
|
|
99
159
|
resource_user = resource_client.User(name)
|
|
100
160
|
try:
|
|
101
|
-
policies[arn] = {
|
|
161
|
+
policies[arn] = {
|
|
162
|
+
p.name: p.policy_document["Statement"]
|
|
163
|
+
for p in resource_user.policies.all()
|
|
164
|
+
}
|
|
102
165
|
except resource_client.meta.client.exceptions.NoSuchEntityException:
|
|
103
166
|
logger.warning(
|
|
104
167
|
f"Could not get policies for user {name} due to NoSuchEntityException; skipping.",
|
|
@@ -107,8 +170,12 @@ def get_user_policy_data(boto3_session: boto3.session.Session, user_list: List[D
|
|
|
107
170
|
|
|
108
171
|
|
|
109
172
|
@timeit
|
|
110
|
-
|
|
111
|
-
|
|
173
|
+
@aws_handle_regions
|
|
174
|
+
def get_user_managed_policy_data(
|
|
175
|
+
boto3_session: boto3.Session,
|
|
176
|
+
user_list: List[Dict],
|
|
177
|
+
) -> Dict:
|
|
178
|
+
resource_client = boto3_session.resource("iam")
|
|
112
179
|
policies = {}
|
|
113
180
|
for user in user_list:
|
|
114
181
|
name = user["UserName"]
|
|
@@ -127,15 +194,22 @@ def get_user_managed_policy_data(boto3_session: boto3.session.Session, user_list
|
|
|
127
194
|
|
|
128
195
|
|
|
129
196
|
@timeit
|
|
130
|
-
|
|
131
|
-
|
|
197
|
+
@aws_handle_regions
|
|
198
|
+
def get_role_policy_data(
|
|
199
|
+
boto3_session: boto3.Session,
|
|
200
|
+
role_list: List[Dict],
|
|
201
|
+
) -> Dict:
|
|
202
|
+
resource_client = boto3_session.resource("iam")
|
|
132
203
|
policies = {}
|
|
133
204
|
for role in role_list:
|
|
134
205
|
name = role["RoleName"]
|
|
135
206
|
arn = role["Arn"]
|
|
136
207
|
resource_role = resource_client.Role(name)
|
|
137
208
|
try:
|
|
138
|
-
policies[arn] = {
|
|
209
|
+
policies[arn] = {
|
|
210
|
+
p.name: p.policy_document["Statement"]
|
|
211
|
+
for p in resource_role.policies.all()
|
|
212
|
+
}
|
|
139
213
|
except resource_client.meta.client.exceptions.NoSuchEntityException:
|
|
140
214
|
logger.warning(
|
|
141
215
|
f"Could not get policies for role {name} due to NoSuchEntityException; skipping.",
|
|
@@ -144,8 +218,12 @@ def get_role_policy_data(boto3_session: boto3.session.Session, role_list: List[D
|
|
|
144
218
|
|
|
145
219
|
|
|
146
220
|
@timeit
|
|
147
|
-
|
|
148
|
-
|
|
221
|
+
@aws_handle_regions
|
|
222
|
+
def get_role_managed_policy_data(
|
|
223
|
+
boto3_session: boto3.Session,
|
|
224
|
+
role_list: List[Dict],
|
|
225
|
+
) -> Dict:
|
|
226
|
+
resource_client = boto3_session.resource("iam")
|
|
149
227
|
policies = {}
|
|
150
228
|
for role in role_list:
|
|
151
229
|
name = role["RoleName"]
|
|
@@ -164,9 +242,10 @@ def get_role_managed_policy_data(boto3_session: boto3.session.Session, role_list
|
|
|
164
242
|
|
|
165
243
|
|
|
166
244
|
@timeit
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
245
|
+
@aws_handle_regions
|
|
246
|
+
def get_role_tags(boto3_session: boto3.Session) -> List[Dict]:
|
|
247
|
+
role_list = get_role_list_data(boto3_session)["Roles"]
|
|
248
|
+
resource_client = boto3_session.resource("iam")
|
|
170
249
|
role_tag_data: List[Dict] = []
|
|
171
250
|
for role in role_list:
|
|
172
251
|
name = role["RoleName"]
|
|
@@ -177,8 +256,8 @@ def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
|
|
|
177
256
|
continue
|
|
178
257
|
|
|
179
258
|
tag_data = {
|
|
180
|
-
|
|
181
|
-
|
|
259
|
+
"ResourceARN": role_arn,
|
|
260
|
+
"Tags": resource_role.tags,
|
|
182
261
|
}
|
|
183
262
|
role_tag_data.append(tag_data)
|
|
184
263
|
|
|
@@ -186,39 +265,66 @@ def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
|
|
|
186
265
|
|
|
187
266
|
|
|
188
267
|
@timeit
|
|
189
|
-
def get_user_list_data(boto3_session: boto3.
|
|
190
|
-
client = boto3_session.client(
|
|
268
|
+
def get_user_list_data(boto3_session: boto3.Session) -> Dict:
|
|
269
|
+
client = boto3_session.client("iam")
|
|
191
270
|
|
|
192
|
-
paginator = client.get_paginator(
|
|
271
|
+
paginator = client.get_paginator("list_users")
|
|
193
272
|
users: List[Dict] = []
|
|
194
273
|
for page in paginator.paginate():
|
|
195
|
-
users.extend(page[
|
|
196
|
-
return {
|
|
274
|
+
users.extend(page["Users"])
|
|
275
|
+
return {"Users": users}
|
|
197
276
|
|
|
198
277
|
|
|
199
278
|
@timeit
|
|
200
|
-
def get_group_list_data(boto3_session: boto3.
|
|
201
|
-
client = boto3_session.client(
|
|
202
|
-
paginator = client.get_paginator(
|
|
279
|
+
def get_group_list_data(boto3_session: boto3.Session) -> Dict:
|
|
280
|
+
client = boto3_session.client("iam")
|
|
281
|
+
paginator = client.get_paginator("list_groups")
|
|
203
282
|
groups: List[Dict] = []
|
|
204
283
|
for page in paginator.paginate():
|
|
205
|
-
groups.extend(page[
|
|
206
|
-
return {
|
|
284
|
+
groups.extend(page["Groups"])
|
|
285
|
+
return {"Groups": groups}
|
|
207
286
|
|
|
208
287
|
|
|
209
288
|
@timeit
|
|
210
|
-
def get_role_list_data(boto3_session: boto3.
|
|
211
|
-
client = boto3_session.client(
|
|
212
|
-
paginator = client.get_paginator(
|
|
289
|
+
def get_role_list_data(boto3_session: boto3.Session) -> Dict:
|
|
290
|
+
client = boto3_session.client("iam")
|
|
291
|
+
paginator = client.get_paginator("list_roles")
|
|
213
292
|
roles: List[Dict] = []
|
|
214
293
|
for page in paginator.paginate():
|
|
215
|
-
roles.extend(page[
|
|
216
|
-
return {
|
|
294
|
+
roles.extend(page["Roles"])
|
|
295
|
+
return {"Roles": roles}
|
|
217
296
|
|
|
218
297
|
|
|
219
298
|
@timeit
|
|
220
|
-
def
|
|
221
|
-
|
|
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
|
+
|
|
322
|
+
@timeit
|
|
323
|
+
def get_account_access_key_data(
|
|
324
|
+
boto3_session: boto3.Session,
|
|
325
|
+
username: str,
|
|
326
|
+
) -> Dict:
|
|
327
|
+
client = boto3_session.client("iam")
|
|
222
328
|
# NOTE we can get away without using a paginator here because users are limited to two access keys
|
|
223
329
|
access_keys: Dict = {}
|
|
224
330
|
try:
|
|
@@ -228,304 +334,327 @@ def get_account_access_key_data(boto3_session: boto3.session.Session, username:
|
|
|
228
334
|
f"Could not get access key for user {username} due to NoSuchEntityException; skipping.",
|
|
229
335
|
)
|
|
230
336
|
return access_keys
|
|
231
|
-
for access_key in access_keys[
|
|
232
|
-
access_key_id = access_key[
|
|
337
|
+
for access_key in access_keys["AccessKeyMetadata"]:
|
|
338
|
+
access_key_id = access_key["AccessKeyId"]
|
|
233
339
|
last_used_info = client.get_access_key_last_used(
|
|
234
340
|
AccessKeyId=access_key_id,
|
|
235
|
-
)[
|
|
341
|
+
)["AccessKeyLastUsed"]
|
|
236
342
|
# only LastUsedDate may be null
|
|
237
343
|
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam/client/get_access_key_last_used.html
|
|
238
|
-
access_key[
|
|
239
|
-
access_key[
|
|
240
|
-
access_key[
|
|
344
|
+
access_key["LastUsedDate"] = last_used_info.get("LastUsedDate")
|
|
345
|
+
access_key["LastUsedService"] = last_used_info["ServiceName"]
|
|
346
|
+
access_key["LastUsedRegion"] = last_used_info["Region"]
|
|
241
347
|
return access_keys
|
|
242
348
|
|
|
243
349
|
|
|
244
350
|
@timeit
|
|
245
|
-
def
|
|
246
|
-
|
|
247
|
-
) ->
|
|
248
|
-
ingest_user = """
|
|
249
|
-
MERGE (unode:AWSUser{arn: $ARN})
|
|
250
|
-
ON CREATE SET unode:AWSPrincipal, unode.userid = $USERID, unode.firstseen = timestamp(),
|
|
251
|
-
unode.createdate = $CREATE_DATE
|
|
252
|
-
SET unode.name = $USERNAME, unode.path = $PATH, unode.passwordlastused = $PASSWORD_LASTUSED,
|
|
253
|
-
unode.lastupdated = $aws_update_tag
|
|
254
|
-
WITH unode
|
|
255
|
-
MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
|
|
256
|
-
MERGE (aa)-[r:RESOURCE]->(unode)
|
|
257
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
258
|
-
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]]:
|
|
259
354
|
"""
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
273
378
|
|
|
274
379
|
|
|
275
380
|
@timeit
|
|
276
|
-
def
|
|
277
|
-
neo4j_session: neo4j.Session,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
381
|
+
def get_policies_for_principal(
|
|
382
|
+
neo4j_session: neo4j.Session,
|
|
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
|
|
288
393
|
"""
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
GROUP_NAME=group["GroupName"],
|
|
297
|
-
PATH=group["Path"],
|
|
298
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
299
|
-
aws_update_tag=aws_update_tag,
|
|
300
|
-
)
|
|
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
|
|
301
401
|
|
|
302
402
|
|
|
303
|
-
def
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
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)
|
|
316
415
|
|
|
416
|
+
return user_data
|
|
317
417
|
|
|
318
|
-
@timeit
|
|
319
|
-
def load_roles(
|
|
320
|
-
neo4j_session: neo4j.Session, roles: List[Dict], current_aws_account_id: str, aws_update_tag: int,
|
|
321
|
-
) -> None:
|
|
322
|
-
ingest_role = """
|
|
323
|
-
MERGE (rnode:AWSPrincipal{arn: $Arn})
|
|
324
|
-
ON CREATE SET rnode.firstseen = timestamp()
|
|
325
|
-
SET
|
|
326
|
-
rnode:AWSRole,
|
|
327
|
-
rnode.roleid = $RoleId,
|
|
328
|
-
rnode.createdate = $CreateDate,
|
|
329
|
-
rnode.name = $RoleName,
|
|
330
|
-
rnode.path = $Path,
|
|
331
|
-
rnode.lastupdated = $aws_update_tag
|
|
332
|
-
WITH rnode
|
|
333
|
-
MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
|
|
334
|
-
MERGE (aa)-[r:RESOURCE]->(rnode)
|
|
335
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
336
|
-
SET r.lastupdated = $aws_update_tag
|
|
337
|
-
"""
|
|
338
418
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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:
|
|
348
461
|
"""
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
#
|
|
352
|
-
# we are agnostic here if this is the AWSAccount is part of the sync scope or
|
|
353
|
-
# a foreign AWS account that contains a trusted principal. The account could also be inscope
|
|
354
|
-
# but not sync yet.
|
|
355
|
-
# - The inscope attribute - set when the account is being sync.
|
|
356
|
-
# - The foreign attribute - the attribute assignment logic is in aws_foreign_accounts.json analysis job
|
|
357
|
-
# - Why seperate statement is needed - the arn may point to service level principals ex - ec2.amazonaws.com
|
|
358
|
-
ingest_spnmap_statement = """
|
|
359
|
-
MERGE (aa:AWSAccount{id: $SpnAccountId})
|
|
360
|
-
ON CREATE SET aa.firstseen = timestamp()
|
|
361
|
-
SET aa.lastupdated = $aws_update_tag
|
|
362
|
-
WITH aa
|
|
363
|
-
MATCH (spnnode:AWSPrincipal{arn: $SpnArn})
|
|
364
|
-
WITH spnnode, aa
|
|
365
|
-
MERGE (aa)-[r:RESOURCE]->(spnnode)
|
|
366
|
-
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.
|
|
367
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]] = []
|
|
368
469
|
|
|
369
|
-
# TODO support conditions
|
|
370
|
-
logger.info(f"Loading {len(roles)} IAM roles to the graph.")
|
|
371
470
|
for role in roles:
|
|
372
|
-
|
|
373
|
-
ingest_role,
|
|
374
|
-
Arn=role["Arn"],
|
|
375
|
-
RoleId=role["RoleId"],
|
|
376
|
-
CreateDate=str(role["CreateDate"]),
|
|
377
|
-
RoleName=role["RoleName"],
|
|
378
|
-
Path=role["Path"],
|
|
379
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
380
|
-
aws_update_tag=aws_update_tag,
|
|
381
|
-
)
|
|
471
|
+
role_arn = role["Arn"]
|
|
382
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
|
|
383
477
|
for statement in role["AssumeRolePolicyDocument"]["Statement"]:
|
|
478
|
+
|
|
384
479
|
principal_entries = _parse_principal_entries(statement["Principal"])
|
|
385
|
-
for principal_type,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
+
}
|
|
400
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
|
+
}
|
|
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
|
+
)
|
|
401
538
|
|
|
402
539
|
|
|
403
540
|
@timeit
|
|
404
|
-
def
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
541
|
+
def load_users(
|
|
542
|
+
neo4j_session: neo4j.Session,
|
|
543
|
+
users: List[Dict],
|
|
544
|
+
current_aws_account_id: str,
|
|
545
|
+
aws_update_tag: int,
|
|
546
|
+
) -> None:
|
|
547
|
+
load(
|
|
548
|
+
neo4j_session,
|
|
549
|
+
AWSUserSchema(),
|
|
550
|
+
users,
|
|
551
|
+
lastupdated=aws_update_tag,
|
|
552
|
+
AWS_ID=current_aws_account_id,
|
|
553
|
+
)
|
|
417
554
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
+
)
|
|
427
570
|
|
|
428
571
|
|
|
429
572
|
@timeit
|
|
430
|
-
def
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
Arn=principal_arn,
|
|
573
|
+
def load_access_keys(
|
|
574
|
+
neo4j_session: neo4j.Session,
|
|
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,
|
|
443
585
|
)
|
|
444
|
-
|
|
445
|
-
|
|
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
|
|
446
601
|
|
|
447
602
|
|
|
448
603
|
@timeit
|
|
449
604
|
def sync_assumerole_relationships(
|
|
450
|
-
neo4j_session: neo4j.Session,
|
|
605
|
+
neo4j_session: neo4j.Session,
|
|
606
|
+
current_aws_account_id: str,
|
|
607
|
+
aws_update_tag: int,
|
|
451
608
|
common_job_parameters: Dict,
|
|
452
609
|
) -> None:
|
|
453
610
|
# Must be called after load_role
|
|
454
|
-
# Computes and syncs the
|
|
455
|
-
logger.info(
|
|
611
|
+
# Computes and syncs the STS_ASSUMEROLE_ALLOW relationship
|
|
612
|
+
logger.info(
|
|
613
|
+
"Syncing assume role mappings for account '%s'.",
|
|
614
|
+
current_aws_account_id,
|
|
615
|
+
)
|
|
456
616
|
query_potential_matches = """
|
|
457
617
|
MATCH (:AWSAccount{id:$AccountId})-[:RESOURCE]->(target:AWSRole)-[:TRUSTS_AWS_PRINCIPAL]->(source:AWSPrincipal)
|
|
458
|
-
WHERE NOT source
|
|
459
|
-
AND NOT source
|
|
460
|
-
AND NOT source
|
|
461
|
-
RETURN target.arn AS target_arn,
|
|
462
|
-
source.arn AS source_arn
|
|
463
|
-
"""
|
|
464
|
-
|
|
465
|
-
ingest_policies_assume_role = """
|
|
466
|
-
MATCH (source:AWSPrincipal{arn: $SourceArn})
|
|
467
|
-
WITH source
|
|
468
|
-
MATCH (role:AWSRole{arn: $TargetArn})
|
|
469
|
-
WITH role, source
|
|
470
|
-
MERGE (source)-[r:STS_ASSUMEROLE_ALLOW]->(role)
|
|
471
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
472
|
-
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
|
|
473
622
|
"""
|
|
474
|
-
|
|
475
|
-
|
|
623
|
+
results = neo4j_session.execute_read(
|
|
624
|
+
read_list_of_dicts_tx,
|
|
476
625
|
query_potential_matches,
|
|
477
626
|
AccountId=current_aws_account_id,
|
|
478
627
|
)
|
|
479
|
-
|
|
480
|
-
|
|
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"]
|
|
481
634
|
policies = get_policies_for_principal(neo4j_session, source_arn)
|
|
482
635
|
if principal_allowed_on_resource(policies, target_arn, ["sts:AssumeRole"]):
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
636
|
+
valid_matches.append(
|
|
637
|
+
{
|
|
638
|
+
"source_arn": source_arn,
|
|
639
|
+
"target_arn": target_arn,
|
|
640
|
+
}
|
|
488
641
|
)
|
|
489
|
-
|
|
490
|
-
|
|
642
|
+
|
|
643
|
+
load_matchlinks(
|
|
491
644
|
neo4j_session,
|
|
492
|
-
|
|
645
|
+
STSAssumeRoleAllowMatchLink(),
|
|
646
|
+
valid_matches,
|
|
647
|
+
lastupdated=aws_update_tag,
|
|
648
|
+
_sub_resource_label="AWSAccount",
|
|
649
|
+
_sub_resource_id=current_aws_account_id,
|
|
493
650
|
)
|
|
494
651
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
WITH user
|
|
502
|
-
MERGE (key:AccountAccessKey{accesskeyid: $AccessKeyId})
|
|
503
|
-
ON CREATE SET key.firstseen = timestamp(), key.createdate = $CreateDate
|
|
504
|
-
SET key.status = $Status,
|
|
505
|
-
key.lastupdated = $aws_update_tag,
|
|
506
|
-
key.lastuseddate = $LastUsedDate,
|
|
507
|
-
key.lastusedservice = $LastUsedService,
|
|
508
|
-
key.lastusedregion = $LastUsedRegion
|
|
509
|
-
WITH user,key
|
|
510
|
-
MERGE (user)-[r:AWS_ACCESS_KEY]->(key)
|
|
511
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
512
|
-
SET r.lastupdated = $aws_update_tag
|
|
513
|
-
"""
|
|
514
|
-
|
|
515
|
-
for arn, access_keys in user_access_keys.items():
|
|
516
|
-
for key in access_keys["AccessKeyMetadata"]:
|
|
517
|
-
if key.get('AccessKeyId'):
|
|
518
|
-
neo4j_session.run(
|
|
519
|
-
ingest_account_key,
|
|
520
|
-
UserARN=arn,
|
|
521
|
-
AccessKeyId=key['AccessKeyId'],
|
|
522
|
-
CreateDate=str(key['CreateDate']),
|
|
523
|
-
Status=key['Status'],
|
|
524
|
-
LastUsedDate=key['LastUsedDate'],
|
|
525
|
-
LastUsedService=key['LastUsedService'],
|
|
526
|
-
LastUsedRegion=key['LastUsedRegion'],
|
|
527
|
-
aws_update_tag=aws_update_tag,
|
|
528
|
-
)
|
|
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)
|
|
529
658
|
|
|
530
659
|
|
|
531
660
|
def ensure_list(obj: Any) -> List[Any]:
|
|
@@ -534,296 +663,681 @@ def ensure_list(obj: Any) -> List[Any]:
|
|
|
534
663
|
return obj
|
|
535
664
|
|
|
536
665
|
|
|
537
|
-
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]] = []
|
|
538
670
|
count = 1
|
|
671
|
+
|
|
539
672
|
if not isinstance(statements, list):
|
|
540
673
|
statements = [statements]
|
|
674
|
+
|
|
541
675
|
for stmt in statements:
|
|
542
|
-
|
|
676
|
+
# Determine statement ID
|
|
677
|
+
if "Sid" in stmt and stmt["Sid"]:
|
|
678
|
+
statement_id = stmt["Sid"]
|
|
679
|
+
else:
|
|
543
680
|
statement_id = count
|
|
544
681
|
count += 1
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
682
|
+
|
|
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
|
|
548
691
|
if "Resource" in stmt:
|
|
549
|
-
|
|
692
|
+
transformed_stmt["Resource"] = ensure_list(stmt["Resource"])
|
|
550
693
|
if "Action" in stmt:
|
|
551
|
-
|
|
694
|
+
transformed_stmt["Action"] = ensure_list(stmt["Action"])
|
|
552
695
|
if "NotAction" in stmt:
|
|
553
|
-
|
|
696
|
+
transformed_stmt["NotAction"] = ensure_list(stmt["NotAction"])
|
|
554
697
|
if "NotResource" in stmt:
|
|
555
|
-
|
|
698
|
+
transformed_stmt["NotResource"] = ensure_list(stmt["NotResource"])
|
|
556
699
|
if "Condition" in stmt:
|
|
557
|
-
|
|
558
|
-
|
|
700
|
+
transformed_stmt["Condition"] = json.dumps(ensure_list(stmt["Condition"]))
|
|
701
|
+
|
|
702
|
+
result.append(transformed_stmt)
|
|
703
|
+
|
|
704
|
+
return result
|
|
559
705
|
|
|
560
706
|
|
|
561
|
-
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
|
+
|
|
562
718
|
for principal_arn, policy_statement_map in policy_map.items():
|
|
563
|
-
logger.debug(f"Transforming IAM {policy_type} policies for principal {principal_arn}")
|
|
564
719
|
for policy_key, statements in policy_statement_map.items():
|
|
565
|
-
policy_id =
|
|
566
|
-
principal_arn,
|
|
567
|
-
policy_type
|
|
568
|
-
policy_key
|
|
569
|
-
)
|
|
570
|
-
|
|
720
|
+
policy_id = (
|
|
721
|
+
transform_policy_id(principal_arn, policy_type, policy_key)
|
|
722
|
+
if policy_type == PolicyType.inline.value
|
|
723
|
+
else policy_key
|
|
724
|
+
)
|
|
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(
|
|
740
|
+
statements,
|
|
741
|
+
policy_id,
|
|
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
|
+
)
|
|
571
774
|
|
|
572
775
|
|
|
573
776
|
def transform_policy_id(principal_arn: str, policy_type: str, name: str) -> str:
|
|
574
777
|
return f"{principal_arn}/{policy_type}_policy/{name}"
|
|
575
778
|
|
|
576
779
|
|
|
577
|
-
def
|
|
578
|
-
|
|
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,
|
|
579
785
|
aws_update_tag: int,
|
|
580
786
|
) -> None:
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
tx.run(
|
|
594
|
-
ingest_policy,
|
|
595
|
-
PolicyId=policy_id,
|
|
596
|
-
PolicyName=policy_name,
|
|
597
|
-
PolicyType=policy_type,
|
|
598
|
-
PrincipalArn=principal_arn,
|
|
599
|
-
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,
|
|
600
799
|
)
|
|
601
800
|
|
|
602
801
|
|
|
603
802
|
@timeit
|
|
604
|
-
def
|
|
605
|
-
neo4j_session: neo4j.Session,
|
|
803
|
+
def load_policy_statements(
|
|
804
|
+
neo4j_session: neo4j.Session,
|
|
805
|
+
statements: list[dict[str, Any]],
|
|
606
806
|
aws_update_tag: int,
|
|
607
807
|
) -> None:
|
|
608
|
-
|
|
808
|
+
load(
|
|
809
|
+
neo4j_session,
|
|
810
|
+
AWSPolicyStatementSchema(),
|
|
811
|
+
statements,
|
|
812
|
+
lastupdated=aws_update_tag,
|
|
813
|
+
POLICY_ID=statements[0]["policy_id"],
|
|
814
|
+
)
|
|
609
815
|
|
|
610
816
|
|
|
611
817
|
@timeit
|
|
612
|
-
def
|
|
613
|
-
neo4j_session: neo4j.Session,
|
|
818
|
+
def _load_policy_statements(
|
|
819
|
+
neo4j_session: neo4j.Session,
|
|
820
|
+
policy_statements: dict[str, list[dict[str, Any]]],
|
|
614
821
|
aws_update_tag: int,
|
|
615
822
|
) -> None:
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
statement.notaction = statement_data.NotAction,
|
|
625
|
-
statement.resource = statement_data.Resource,
|
|
626
|
-
statement.notresource = statement_data.NotResource,
|
|
627
|
-
statement.condition = statement_data.Condition,
|
|
628
|
-
statement.sid = statement_data.Sid,
|
|
629
|
-
statement.lastupdated = $aws_update_tag
|
|
630
|
-
MERGE (policy)-[r:STATEMENT]->(statement)
|
|
631
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
632
|
-
SET r.lastupdated = $aws_update_tag
|
|
633
|
-
"""
|
|
634
|
-
neo4j_session.run(
|
|
635
|
-
ingest_policy_statement,
|
|
636
|
-
PolicyId=policy_id,
|
|
637
|
-
PolicyName=policy_name,
|
|
638
|
-
Statements=statements,
|
|
639
|
-
aws_update_tag=aws_update_tag,
|
|
640
|
-
).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
|
+
)
|
|
641
831
|
|
|
642
832
|
|
|
643
833
|
@timeit
|
|
644
834
|
def load_policy_data(
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
835
|
+
neo4j_session: neo4j.Session,
|
|
836
|
+
transformed_policy_data: TransformedPolicyData,
|
|
837
|
+
aws_update_tag: int,
|
|
838
|
+
current_aws_account_id: str,
|
|
649
839
|
) -> None:
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
+
)
|
|
661
853
|
|
|
662
854
|
|
|
663
855
|
@timeit
|
|
664
856
|
def sync_users(
|
|
665
|
-
neo4j_session: neo4j.Session,
|
|
666
|
-
|
|
857
|
+
neo4j_session: neo4j.Session,
|
|
858
|
+
boto3_session: boto3.Session,
|
|
859
|
+
current_aws_account_id: str,
|
|
860
|
+
aws_update_tag: int,
|
|
861
|
+
common_job_parameters: Dict,
|
|
667
862
|
) -> None:
|
|
668
863
|
logger.info("Syncing IAM users for account '%s'.", current_aws_account_id)
|
|
669
864
|
data = get_user_list_data(boto3_session)
|
|
670
|
-
|
|
865
|
+
user_data = transform_users(data["Users"])
|
|
866
|
+
load_users(neo4j_session, user_data, current_aws_account_id, aws_update_tag)
|
|
867
|
+
|
|
868
|
+
sync_user_inline_policies(
|
|
869
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
870
|
+
)
|
|
871
|
+
|
|
872
|
+
sync_user_managed_policies(
|
|
873
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
874
|
+
)
|
|
671
875
|
|
|
672
|
-
sync_user_inline_policies(boto3_session, data, neo4j_session, aws_update_tag)
|
|
673
876
|
|
|
674
|
-
|
|
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
|
+
)
|
|
675
888
|
|
|
676
|
-
|
|
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
|
|
907
|
+
)
|
|
677
908
|
|
|
678
909
|
|
|
679
910
|
@timeit
|
|
680
911
|
def sync_user_managed_policies(
|
|
681
|
-
boto3_session: boto3.
|
|
912
|
+
boto3_session: boto3.Session,
|
|
913
|
+
data: Dict,
|
|
914
|
+
neo4j_session: neo4j.Session,
|
|
682
915
|
aws_update_tag: int,
|
|
916
|
+
current_aws_account_id: str,
|
|
683
917
|
) -> None:
|
|
684
|
-
managed_policy_data = get_user_managed_policy_data(boto3_session, data[
|
|
685
|
-
transform_policy_data(
|
|
686
|
-
|
|
918
|
+
managed_policy_data = get_user_managed_policy_data(boto3_session, data["Users"])
|
|
919
|
+
transformed_policy_data = transform_policy_data(
|
|
920
|
+
managed_policy_data, PolicyType.managed.value
|
|
921
|
+
)
|
|
922
|
+
load_policy_data(
|
|
923
|
+
neo4j_session,
|
|
924
|
+
transformed_policy_data,
|
|
925
|
+
aws_update_tag,
|
|
926
|
+
current_aws_account_id,
|
|
927
|
+
)
|
|
687
928
|
|
|
688
929
|
|
|
689
930
|
@timeit
|
|
690
931
|
def sync_user_inline_policies(
|
|
691
|
-
boto3_session: boto3.
|
|
932
|
+
boto3_session: boto3.Session,
|
|
933
|
+
data: Dict,
|
|
934
|
+
neo4j_session: neo4j.Session,
|
|
692
935
|
aws_update_tag: int,
|
|
936
|
+
current_aws_account_id: str,
|
|
693
937
|
) -> None:
|
|
694
|
-
policy_data = get_user_policy_data(boto3_session, data[
|
|
695
|
-
transform_policy_data(
|
|
696
|
-
|
|
938
|
+
policy_data = get_user_policy_data(boto3_session, data["Users"])
|
|
939
|
+
transformed_policy_data = transform_policy_data(
|
|
940
|
+
policy_data, PolicyType.inline.value
|
|
941
|
+
)
|
|
942
|
+
load_policy_data(
|
|
943
|
+
neo4j_session,
|
|
944
|
+
transformed_policy_data,
|
|
945
|
+
aws_update_tag,
|
|
946
|
+
current_aws_account_id,
|
|
947
|
+
)
|
|
697
948
|
|
|
698
949
|
|
|
699
950
|
@timeit
|
|
700
951
|
def sync_groups(
|
|
701
|
-
neo4j_session: neo4j.Session,
|
|
702
|
-
|
|
952
|
+
neo4j_session: neo4j.Session,
|
|
953
|
+
boto3_session: boto3.Session,
|
|
954
|
+
current_aws_account_id: str,
|
|
955
|
+
aws_update_tag: int,
|
|
956
|
+
common_job_parameters: Dict,
|
|
703
957
|
) -> None:
|
|
704
958
|
logger.info("Syncing IAM groups for account '%s'.", current_aws_account_id)
|
|
705
959
|
data = get_group_list_data(boto3_session)
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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)
|
|
709
963
|
|
|
710
|
-
|
|
964
|
+
sync_groups_inline_policies(
|
|
965
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
966
|
+
)
|
|
711
967
|
|
|
712
|
-
|
|
968
|
+
sync_group_managed_policies(
|
|
969
|
+
boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
|
|
970
|
+
)
|
|
713
971
|
|
|
714
972
|
|
|
715
973
|
def sync_group_managed_policies(
|
|
716
|
-
boto3_session: boto3.
|
|
974
|
+
boto3_session: boto3.Session,
|
|
975
|
+
data: Dict,
|
|
976
|
+
neo4j_session: neo4j.Session,
|
|
717
977
|
aws_update_tag: int,
|
|
978
|
+
current_aws_account_id: str,
|
|
718
979
|
) -> None:
|
|
719
980
|
managed_policy_data = get_group_managed_policy_data(boto3_session, data["Groups"])
|
|
720
|
-
transform_policy_data(
|
|
721
|
-
|
|
981
|
+
transformed_policy_data = transform_policy_data(
|
|
982
|
+
managed_policy_data, PolicyType.managed.value
|
|
983
|
+
)
|
|
984
|
+
load_policy_data(
|
|
985
|
+
neo4j_session,
|
|
986
|
+
transformed_policy_data,
|
|
987
|
+
aws_update_tag,
|
|
988
|
+
current_aws_account_id,
|
|
989
|
+
)
|
|
722
990
|
|
|
723
991
|
|
|
724
992
|
def sync_groups_inline_policies(
|
|
725
|
-
boto3_session: boto3.
|
|
993
|
+
boto3_session: boto3.Session,
|
|
994
|
+
data: Dict,
|
|
995
|
+
neo4j_session: neo4j.Session,
|
|
726
996
|
aws_update_tag: int,
|
|
997
|
+
current_aws_account_id: str,
|
|
727
998
|
) -> None:
|
|
728
999
|
policy_data = get_group_policy_data(boto3_session, data["Groups"])
|
|
729
|
-
transform_policy_data(
|
|
730
|
-
|
|
1000
|
+
transformed_policy_data = transform_policy_data(
|
|
1001
|
+
policy_data, PolicyType.inline.value
|
|
1002
|
+
)
|
|
1003
|
+
load_policy_data(
|
|
1004
|
+
neo4j_session,
|
|
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,
|
|
1101
|
+
aws_update_tag,
|
|
1102
|
+
)
|
|
1103
|
+
load_role_data(
|
|
1104
|
+
neo4j_session, transformed.role_data, current_aws_account_id, aws_update_tag
|
|
1105
|
+
)
|
|
731
1106
|
|
|
732
1107
|
|
|
733
1108
|
@timeit
|
|
734
1109
|
def sync_roles(
|
|
735
|
-
neo4j_session: neo4j.Session,
|
|
736
|
-
|
|
1110
|
+
neo4j_session: neo4j.Session,
|
|
1111
|
+
boto3_session: boto3.Session,
|
|
1112
|
+
current_aws_account_id: str,
|
|
1113
|
+
aws_update_tag: int,
|
|
1114
|
+
common_job_parameters: Dict,
|
|
737
1115
|
) -> None:
|
|
738
1116
|
logger.info("Syncing IAM roles for account '%s'.", current_aws_account_id)
|
|
739
1117
|
data = get_role_list_data(boto3_session)
|
|
740
|
-
load_roles(neo4j_session, data['Roles'], current_aws_account_id, aws_update_tag)
|
|
741
1118
|
|
|
742
|
-
|
|
1119
|
+
sync_role_assumptions(neo4j_session, data, current_aws_account_id, aws_update_tag)
|
|
743
1120
|
|
|
744
|
-
|
|
1121
|
+
sync_role_inline_policies(
|
|
1122
|
+
current_aws_account_id,
|
|
1123
|
+
boto3_session,
|
|
1124
|
+
data,
|
|
1125
|
+
neo4j_session,
|
|
1126
|
+
aws_update_tag,
|
|
1127
|
+
)
|
|
745
1128
|
|
|
746
|
-
|
|
1129
|
+
sync_role_managed_policies(
|
|
1130
|
+
current_aws_account_id,
|
|
1131
|
+
boto3_session,
|
|
1132
|
+
data,
|
|
1133
|
+
neo4j_session,
|
|
1134
|
+
aws_update_tag,
|
|
1135
|
+
)
|
|
747
1136
|
|
|
748
1137
|
|
|
749
1138
|
def sync_role_managed_policies(
|
|
750
|
-
current_aws_account_id: str,
|
|
751
|
-
|
|
1139
|
+
current_aws_account_id: str,
|
|
1140
|
+
boto3_session: boto3.Session,
|
|
1141
|
+
data: Dict,
|
|
1142
|
+
neo4j_session: neo4j.Session,
|
|
1143
|
+
aws_update_tag: int,
|
|
752
1144
|
) -> None:
|
|
753
|
-
logger.info(
|
|
1145
|
+
logger.info(
|
|
1146
|
+
"Syncing IAM role managed policies for account '%s'.",
|
|
1147
|
+
current_aws_account_id,
|
|
1148
|
+
)
|
|
754
1149
|
managed_policy_data = get_role_managed_policy_data(boto3_session, data["Roles"])
|
|
755
|
-
transform_policy_data(
|
|
756
|
-
|
|
1150
|
+
transformed_policy_data = transform_policy_data(
|
|
1151
|
+
managed_policy_data, PolicyType.managed.value
|
|
1152
|
+
)
|
|
1153
|
+
load_policy_data(
|
|
1154
|
+
neo4j_session,
|
|
1155
|
+
transformed_policy_data,
|
|
1156
|
+
aws_update_tag,
|
|
1157
|
+
current_aws_account_id,
|
|
1158
|
+
)
|
|
757
1159
|
|
|
758
1160
|
|
|
759
1161
|
def sync_role_inline_policies(
|
|
760
|
-
current_aws_account_id: str,
|
|
761
|
-
|
|
1162
|
+
current_aws_account_id: str,
|
|
1163
|
+
boto3_session: boto3.Session,
|
|
1164
|
+
data: Dict,
|
|
1165
|
+
neo4j_session: neo4j.Session,
|
|
1166
|
+
aws_update_tag: int,
|
|
762
1167
|
) -> None:
|
|
763
|
-
logger.info(
|
|
1168
|
+
logger.info(
|
|
1169
|
+
"Syncing IAM role inline policies for account '%s'.",
|
|
1170
|
+
current_aws_account_id,
|
|
1171
|
+
)
|
|
764
1172
|
inline_policy_data = get_role_policy_data(boto3_session, data["Roles"])
|
|
765
|
-
transform_policy_data(
|
|
766
|
-
|
|
1173
|
+
transformed_policy_data = transform_policy_data(
|
|
1174
|
+
inline_policy_data, PolicyType.inline.value
|
|
1175
|
+
)
|
|
1176
|
+
load_policy_data(
|
|
1177
|
+
neo4j_session,
|
|
1178
|
+
transformed_policy_data,
|
|
1179
|
+
aws_update_tag,
|
|
1180
|
+
current_aws_account_id,
|
|
1181
|
+
)
|
|
1182
|
+
|
|
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
|
+
]
|
|
767
1217
|
|
|
768
1218
|
|
|
769
1219
|
@timeit
|
|
770
|
-
def
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
logger.info("Syncing IAM group membership for account '%s'.", current_aws_account_id)
|
|
775
|
-
query = "MATCH (group:AWSGroup)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ACCOUNT_ID}) " \
|
|
776
|
-
"return group.name as name, group.arn as arn;"
|
|
777
|
-
groups = neo4j_session.run(query, AWS_ACCOUNT_ID=current_aws_account_id)
|
|
778
|
-
groups_membership = {group["arn"]: get_group_membership_data(boto3_session, group["name"]) for group in groups}
|
|
779
|
-
load_group_memberships(neo4j_session, groups_membership, aws_update_tag)
|
|
780
|
-
run_cleanup_job(
|
|
781
|
-
'aws_import_groups_membership_cleanup.json',
|
|
782
|
-
neo4j_session,
|
|
783
|
-
common_job_parameters,
|
|
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"]
|
|
784
1224
|
)
|
|
785
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
|
+
)
|
|
786
1234
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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
|
|
1242
|
+
)
|
|
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
|
+
)
|
|
1249
|
+
|
|
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
|
|
1254
|
+
)
|
|
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
|
|
1261
|
+
)
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
def sync_root_principal(
|
|
1265
|
+
neo4j_session: neo4j.Session, current_aws_account_id: str, aws_update_tag: int
|
|
791
1266
|
) -> None:
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
'aws_import_account_access_key_cleanup.json',
|
|
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(
|
|
802
1276
|
neo4j_session,
|
|
803
|
-
|
|
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,
|
|
804
1281
|
)
|
|
805
1282
|
|
|
806
1283
|
|
|
807
1284
|
@timeit
|
|
808
1285
|
def sync(
|
|
809
|
-
neo4j_session: neo4j.Session,
|
|
810
|
-
|
|
1286
|
+
neo4j_session: neo4j.Session,
|
|
1287
|
+
boto3_session: boto3.Session,
|
|
1288
|
+
regions: List[str],
|
|
1289
|
+
current_aws_account_id: str,
|
|
1290
|
+
update_tag: int,
|
|
1291
|
+
common_job_parameters: Dict,
|
|
811
1292
|
) -> None:
|
|
812
1293
|
logger.info("Syncing IAM for account '%s'.", current_aws_account_id)
|
|
813
1294
|
# This module only syncs IAM information that is in use.
|
|
814
1295
|
# As such only policies that are attached to a user, role or group are synced
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1296
|
+
sync_root_principal(
|
|
1297
|
+
neo4j_session,
|
|
1298
|
+
current_aws_account_id,
|
|
1299
|
+
update_tag,
|
|
1300
|
+
)
|
|
1301
|
+
sync_users(
|
|
1302
|
+
neo4j_session,
|
|
1303
|
+
boto3_session,
|
|
1304
|
+
current_aws_account_id,
|
|
1305
|
+
update_tag,
|
|
1306
|
+
common_job_parameters,
|
|
1307
|
+
)
|
|
1308
|
+
sync_groups(
|
|
1309
|
+
neo4j_session,
|
|
1310
|
+
boto3_session,
|
|
1311
|
+
current_aws_account_id,
|
|
1312
|
+
update_tag,
|
|
1313
|
+
common_job_parameters,
|
|
1314
|
+
)
|
|
1315
|
+
sync_roles(
|
|
1316
|
+
neo4j_session,
|
|
1317
|
+
boto3_session,
|
|
1318
|
+
current_aws_account_id,
|
|
1319
|
+
update_tag,
|
|
1320
|
+
common_job_parameters,
|
|
1321
|
+
)
|
|
1322
|
+
sync_assumerole_relationships(
|
|
1323
|
+
neo4j_session,
|
|
1324
|
+
current_aws_account_id,
|
|
1325
|
+
update_tag,
|
|
1326
|
+
common_job_parameters,
|
|
1327
|
+
)
|
|
1328
|
+
sync_user_access_keys(
|
|
1329
|
+
neo4j_session,
|
|
1330
|
+
boto3_session,
|
|
1331
|
+
current_aws_account_id,
|
|
1332
|
+
update_tag,
|
|
1333
|
+
common_job_parameters,
|
|
1334
|
+
)
|
|
1335
|
+
cleanup_iam(neo4j_session, common_job_parameters)
|
|
822
1336
|
merge_module_sync_metadata(
|
|
823
1337
|
neo4j_session,
|
|
824
|
-
group_type=
|
|
1338
|
+
group_type="AWSAccount",
|
|
825
1339
|
group_id=current_aws_account_id,
|
|
826
|
-
synced_type=
|
|
1340
|
+
synced_type="AWSPrincipal",
|
|
827
1341
|
update_tag=update_tag,
|
|
828
1342
|
stat_handler=stat_handler,
|
|
829
1343
|
)
|