cartography 0.102.0rc2__py3-none-any.whl → 0.103.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.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +376 -249
- cartography/client/core/tx.py +39 -18
- cartography/config.py +28 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -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 +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/cloudwatch.py +93 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +138 -98
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/efs.py +93 -0
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +57 -46
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- 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 +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- 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 +36 -10
- 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 +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- 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 +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +90 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +94 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +78 -0
- cartography/intel/openai/util.py +29 -0
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- 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/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- 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 +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -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 +44 -34
- cartography/models/aws/ec2/route_tables.py +50 -43
- cartography/models/aws/ec2/routes.py +45 -37
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/efs/__init__.py +0 -0
- cartography/models/aws/efs/mount_target.py +52 -0
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- 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 +82 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- 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 +56 -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 +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/openai/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +90 -0
- cartography/models/openai/apikey.py +84 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +70 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +49 -0
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/models/tailscale/__init__.py +0 -0
- cartography/models/tailscale/device.py +95 -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 +52 -0
- cartography/stats.py +3 -3
- cartography/sync.py +113 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
- cartography-0.103.0.dist-info/RECORD +442 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc2.dist-info/RECORD +0 -381
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
cartography/intel/aws/kms.py
CHANGED
|
@@ -23,18 +23,23 @@ logger = logging.getLogger(__name__)
|
|
|
23
23
|
@timeit
|
|
24
24
|
@aws_handle_regions
|
|
25
25
|
def get_kms_key_list(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
|
|
26
|
-
client = boto3_session.client(
|
|
27
|
-
paginator = client.get_paginator(
|
|
26
|
+
client = boto3_session.client("kms", region_name=region)
|
|
27
|
+
paginator = client.get_paginator("list_keys")
|
|
28
28
|
key_list: List[Any] = []
|
|
29
29
|
for page in paginator.paginate():
|
|
30
|
-
key_list.extend(page[
|
|
30
|
+
key_list.extend(page["Keys"])
|
|
31
31
|
|
|
32
32
|
described_key_list = []
|
|
33
33
|
for key in key_list:
|
|
34
34
|
try:
|
|
35
|
-
response = client.describe_key(KeyId=key["KeyId"])[
|
|
35
|
+
response = client.describe_key(KeyId=key["KeyId"])["KeyMetadata"]
|
|
36
36
|
except ClientError as e:
|
|
37
|
-
logger.warning(
|
|
37
|
+
logger.warning(
|
|
38
|
+
"Failed to describe key with key id - {}. Error - {}".format(
|
|
39
|
+
key["KeyId"],
|
|
40
|
+
e,
|
|
41
|
+
),
|
|
42
|
+
)
|
|
38
43
|
continue
|
|
39
44
|
|
|
40
45
|
described_key_list.append(response)
|
|
@@ -45,17 +50,19 @@ def get_kms_key_list(boto3_session: boto3.session.Session, region: str) -> List[
|
|
|
45
50
|
@timeit
|
|
46
51
|
@aws_handle_regions
|
|
47
52
|
def get_kms_key_details(
|
|
48
|
-
boto3_session: boto3.session.Session,
|
|
53
|
+
boto3_session: boto3.session.Session,
|
|
54
|
+
kms_key_data: Dict,
|
|
55
|
+
region: str,
|
|
49
56
|
) -> Generator[Any, Any, Any]:
|
|
50
57
|
"""
|
|
51
58
|
Iterates over all KMS Keys.
|
|
52
59
|
"""
|
|
53
|
-
client = boto3_session.client(
|
|
60
|
+
client = boto3_session.client("kms", region_name=region)
|
|
54
61
|
for key in kms_key_data:
|
|
55
62
|
policy = get_policy(key, client)
|
|
56
63
|
aliases = get_aliases(key, client)
|
|
57
64
|
grants = get_grants(key, client)
|
|
58
|
-
yield key[
|
|
65
|
+
yield key["KeyId"], policy, aliases, grants
|
|
59
66
|
|
|
60
67
|
|
|
61
68
|
@timeit
|
|
@@ -64,10 +71,10 @@ def get_policy(key: Dict, client: botocore.client.BaseClient) -> Any:
|
|
|
64
71
|
Gets the KMS Key policy. Returns policy string or None if we are unable to retrieve it.
|
|
65
72
|
"""
|
|
66
73
|
try:
|
|
67
|
-
policy = client.get_key_policy(KeyId=key["KeyId"], PolicyName=
|
|
74
|
+
policy = client.get_key_policy(KeyId=key["KeyId"], PolicyName="default")
|
|
68
75
|
except ClientError as e:
|
|
69
76
|
policy = None
|
|
70
|
-
if e.response[
|
|
77
|
+
if e.response["Error"]["Code"] == "AccessDeniedException":
|
|
71
78
|
logger.warning(
|
|
72
79
|
f"kms:get_key_policy on key id {key['KeyId']} failed with AccessDeniedException; continuing sync.",
|
|
73
80
|
exc_info=True,
|
|
@@ -84,9 +91,9 @@ def get_aliases(key: Dict, client: botocore.client.BaseClient) -> List[Any]:
|
|
|
84
91
|
Gets the KMS Key Aliases.
|
|
85
92
|
"""
|
|
86
93
|
aliases: List[Any] = []
|
|
87
|
-
paginator = client.get_paginator(
|
|
88
|
-
for page in paginator.paginate(KeyId=key[
|
|
89
|
-
aliases.extend(page[
|
|
94
|
+
paginator = client.get_paginator("list_aliases")
|
|
95
|
+
for page in paginator.paginate(KeyId=key["KeyId"]):
|
|
96
|
+
aliases.extend(page["Aliases"])
|
|
90
97
|
|
|
91
98
|
return aliases
|
|
92
99
|
|
|
@@ -97,12 +104,12 @@ def get_grants(key: Dict, client: botocore.client.BaseClient) -> List[Any]:
|
|
|
97
104
|
Gets the KMS Key Grants.
|
|
98
105
|
"""
|
|
99
106
|
grants: List[Any] = []
|
|
100
|
-
paginator = client.get_paginator(
|
|
107
|
+
paginator = client.get_paginator("list_grants")
|
|
101
108
|
try:
|
|
102
|
-
for page in paginator.paginate(KeyId=key[
|
|
103
|
-
grants.extend(page[
|
|
109
|
+
for page in paginator.paginate(KeyId=key["KeyId"]):
|
|
110
|
+
grants.extend(page["Grants"])
|
|
104
111
|
except ClientError as e:
|
|
105
|
-
if e.response[
|
|
112
|
+
if e.response["Error"]["Code"] == "AccessDeniedException":
|
|
106
113
|
logger.warning(
|
|
107
114
|
f'kms:list_grants on key_id {key["KeyId"]} failed with AccessDeniedException; continuing sync.',
|
|
108
115
|
exc_info=True,
|
|
@@ -113,7 +120,11 @@ def get_grants(key: Dict, client: botocore.client.BaseClient) -> List[Any]:
|
|
|
113
120
|
|
|
114
121
|
|
|
115
122
|
@timeit
|
|
116
|
-
def _load_kms_key_aliases(
|
|
123
|
+
def _load_kms_key_aliases(
|
|
124
|
+
neo4j_session: neo4j.Session,
|
|
125
|
+
aliases: List[Dict],
|
|
126
|
+
update_tag: int,
|
|
127
|
+
) -> None:
|
|
117
128
|
"""
|
|
118
129
|
Ingest KMS Aliases into neo4j.
|
|
119
130
|
"""
|
|
@@ -137,7 +148,11 @@ def _load_kms_key_aliases(neo4j_session: neo4j.Session, aliases: List[Dict], upd
|
|
|
137
148
|
|
|
138
149
|
|
|
139
150
|
@timeit
|
|
140
|
-
def _load_kms_key_grants(
|
|
151
|
+
def _load_kms_key_grants(
|
|
152
|
+
neo4j_session: neo4j.Session,
|
|
153
|
+
grants_list: List[Dict],
|
|
154
|
+
update_tag: int,
|
|
155
|
+
) -> None:
|
|
141
156
|
"""
|
|
142
157
|
Ingest KMS Key Grants into neo4j.
|
|
143
158
|
"""
|
|
@@ -157,7 +172,7 @@ def _load_kms_key_grants(neo4j_session: neo4j.Session, grants_list: List[Dict],
|
|
|
157
172
|
# neo4j does not accept datetime objects and values. This loop is used to convert
|
|
158
173
|
# these values to string.
|
|
159
174
|
for grant in grants_list:
|
|
160
|
-
grant[
|
|
175
|
+
grant["CreationDate"] = str(grant["CreationDate"])
|
|
161
176
|
|
|
162
177
|
neo4j_session.run(
|
|
163
178
|
ingest_grants,
|
|
@@ -167,7 +182,11 @@ def _load_kms_key_grants(neo4j_session: neo4j.Session, grants_list: List[Dict],
|
|
|
167
182
|
|
|
168
183
|
|
|
169
184
|
@timeit
|
|
170
|
-
def _load_kms_key_policies(
|
|
185
|
+
def _load_kms_key_policies(
|
|
186
|
+
neo4j_session: neo4j.Session,
|
|
187
|
+
policies: List[Dict],
|
|
188
|
+
update_tag: int,
|
|
189
|
+
) -> None:
|
|
171
190
|
"""
|
|
172
191
|
Ingest KMS Key policy results into neo4j.
|
|
173
192
|
"""
|
|
@@ -201,8 +220,11 @@ def _set_default_values(neo4j_session: neo4j.Session, aws_account_id: str) -> No
|
|
|
201
220
|
|
|
202
221
|
@timeit
|
|
203
222
|
def load_kms_key_details(
|
|
204
|
-
|
|
205
|
-
|
|
223
|
+
neo4j_session: neo4j.Session,
|
|
224
|
+
policy_alias_grants_data: List[Tuple[Any, Any, Any, Any]],
|
|
225
|
+
region: str,
|
|
226
|
+
aws_account_id: str,
|
|
227
|
+
update_tag: int,
|
|
206
228
|
) -> None:
|
|
207
229
|
"""
|
|
208
230
|
Create dictionaries for all KMS key policies, aliases and grants so we can import them in a single query for each
|
|
@@ -221,9 +243,9 @@ def load_kms_key_details(
|
|
|
221
243
|
|
|
222
244
|
# cleanup existing policy properties
|
|
223
245
|
run_cleanup_job(
|
|
224
|
-
|
|
246
|
+
"aws_kms_details.json",
|
|
225
247
|
neo4j_session,
|
|
226
|
-
{
|
|
248
|
+
{"UPDATE_TAG": update_tag, "AWS_ID": aws_account_id},
|
|
227
249
|
)
|
|
228
250
|
|
|
229
251
|
_load_kms_key_policies(neo4j_session, policies, update_tag)
|
|
@@ -281,7 +303,7 @@ def parse_policy(key: str, policy: Policy) -> Optional[Dict[Any, Any]]:
|
|
|
281
303
|
# }
|
|
282
304
|
if policy is not None:
|
|
283
305
|
# get just the policy element and convert to JSON because boto3 returns this as string
|
|
284
|
-
policy = Policy(json.loads(policy[
|
|
306
|
+
policy = Policy(json.loads(policy["Policy"]))
|
|
285
307
|
if policy.is_internet_accessible():
|
|
286
308
|
return {
|
|
287
309
|
"kms_key": key,
|
|
@@ -296,7 +318,10 @@ def parse_policy(key: str, policy: Policy) -> Optional[Dict[Any, Any]]:
|
|
|
296
318
|
|
|
297
319
|
@timeit
|
|
298
320
|
def load_kms_keys(
|
|
299
|
-
neo4j_session: neo4j.Session,
|
|
321
|
+
neo4j_session: neo4j.Session,
|
|
322
|
+
data: Dict,
|
|
323
|
+
region: str,
|
|
324
|
+
current_aws_account_id: str,
|
|
300
325
|
aws_update_tag: int,
|
|
301
326
|
) -> None:
|
|
302
327
|
ingest_keys = """
|
|
@@ -322,9 +347,9 @@ def load_kms_keys(
|
|
|
322
347
|
# neo4j does not accept datetime objects and values. This loop is used to convert
|
|
323
348
|
# these values to string.
|
|
324
349
|
for key in data:
|
|
325
|
-
key[
|
|
326
|
-
key[
|
|
327
|
-
key[
|
|
350
|
+
key["CreationDate"] = str(key["CreationDate"])
|
|
351
|
+
key["DeletionDate"] = str(key.get("DeletionDate"))
|
|
352
|
+
key["ValidTo"] = str(key.get("ValidTo"))
|
|
328
353
|
|
|
329
354
|
neo4j_session.run(
|
|
330
355
|
ingest_keys,
|
|
@@ -337,29 +362,58 @@ def load_kms_keys(
|
|
|
337
362
|
|
|
338
363
|
@timeit
|
|
339
364
|
def cleanup_kms(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
|
|
340
|
-
run_cleanup_job(
|
|
365
|
+
run_cleanup_job("aws_import_kms_cleanup.json", neo4j_session, common_job_parameters)
|
|
341
366
|
|
|
342
367
|
|
|
343
368
|
@timeit
|
|
344
369
|
def sync_kms_keys(
|
|
345
|
-
neo4j_session: neo4j.Session,
|
|
370
|
+
neo4j_session: neo4j.Session,
|
|
371
|
+
boto3_session: boto3.session.Session,
|
|
372
|
+
region: str,
|
|
373
|
+
current_aws_account_id: str,
|
|
346
374
|
aws_update_tag: int,
|
|
347
375
|
) -> None:
|
|
348
376
|
kms_keys = get_kms_key_list(boto3_session, region)
|
|
349
377
|
|
|
350
|
-
load_kms_keys(
|
|
378
|
+
load_kms_keys(
|
|
379
|
+
neo4j_session,
|
|
380
|
+
kms_keys,
|
|
381
|
+
region,
|
|
382
|
+
current_aws_account_id,
|
|
383
|
+
aws_update_tag,
|
|
384
|
+
)
|
|
351
385
|
|
|
352
386
|
policy_alias_grants_data = get_kms_key_details(boto3_session, kms_keys, region)
|
|
353
|
-
load_kms_key_details(
|
|
387
|
+
load_kms_key_details(
|
|
388
|
+
neo4j_session,
|
|
389
|
+
policy_alias_grants_data,
|
|
390
|
+
region,
|
|
391
|
+
current_aws_account_id,
|
|
392
|
+
aws_update_tag,
|
|
393
|
+
)
|
|
354
394
|
|
|
355
395
|
|
|
356
396
|
@timeit
|
|
357
397
|
def sync(
|
|
358
|
-
neo4j_session: neo4j.Session,
|
|
359
|
-
|
|
398
|
+
neo4j_session: neo4j.Session,
|
|
399
|
+
boto3_session: boto3.session.Session,
|
|
400
|
+
regions: List[str],
|
|
401
|
+
current_aws_account_id: str,
|
|
402
|
+
update_tag: int,
|
|
403
|
+
common_job_parameters: Dict,
|
|
360
404
|
) -> None:
|
|
361
405
|
for region in regions:
|
|
362
|
-
logger.info(
|
|
363
|
-
|
|
406
|
+
logger.info(
|
|
407
|
+
"Syncing KMS for region %s in account '%s'.",
|
|
408
|
+
region,
|
|
409
|
+
current_aws_account_id,
|
|
410
|
+
)
|
|
411
|
+
sync_kms_keys(
|
|
412
|
+
neo4j_session,
|
|
413
|
+
boto3_session,
|
|
414
|
+
region,
|
|
415
|
+
current_aws_account_id,
|
|
416
|
+
update_tag,
|
|
417
|
+
)
|
|
364
418
|
|
|
365
419
|
cleanup_kms(neo4j_session, common_job_parameters)
|
|
@@ -21,18 +21,22 @@ def get_lambda_data(boto3_session: boto3.session.Session, region: str) -> List[D
|
|
|
21
21
|
"""
|
|
22
22
|
Create an Lambda boto3 client and grab all the lambda functions.
|
|
23
23
|
"""
|
|
24
|
-
client = boto3_session.client(
|
|
25
|
-
paginator = client.get_paginator(
|
|
24
|
+
client = boto3_session.client("lambda", region_name=region)
|
|
25
|
+
paginator = client.get_paginator("list_functions")
|
|
26
26
|
lambda_functions = []
|
|
27
27
|
for page in paginator.paginate():
|
|
28
|
-
for each_function in page[
|
|
28
|
+
for each_function in page["Functions"]:
|
|
29
29
|
lambda_functions.append(each_function)
|
|
30
30
|
return lambda_functions
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
@timeit
|
|
34
34
|
def load_lambda_functions(
|
|
35
|
-
|
|
35
|
+
neo4j_session: neo4j.Session,
|
|
36
|
+
data: List[Dict],
|
|
37
|
+
region: str,
|
|
38
|
+
current_aws_account_id: str,
|
|
39
|
+
aws_update_tag: int,
|
|
36
40
|
) -> None:
|
|
37
41
|
ingest_lambda_functions = """
|
|
38
42
|
UNWIND $lambda_functions_list AS lf
|
|
@@ -86,22 +90,28 @@ def load_lambda_functions(
|
|
|
86
90
|
|
|
87
91
|
@timeit
|
|
88
92
|
@aws_handle_regions
|
|
89
|
-
def get_function_aliases(
|
|
93
|
+
def get_function_aliases(
|
|
94
|
+
lambda_function: Dict,
|
|
95
|
+
client: botocore.client.BaseClient,
|
|
96
|
+
) -> List[Any]:
|
|
90
97
|
aliases: List[Any] = []
|
|
91
|
-
paginator = client.get_paginator(
|
|
92
|
-
for page in paginator.paginate(FunctionName=lambda_function[
|
|
93
|
-
aliases.extend(page[
|
|
98
|
+
paginator = client.get_paginator("list_aliases")
|
|
99
|
+
for page in paginator.paginate(FunctionName=lambda_function["FunctionName"]):
|
|
100
|
+
aliases.extend(page["Aliases"])
|
|
94
101
|
|
|
95
102
|
return aliases
|
|
96
103
|
|
|
97
104
|
|
|
98
105
|
@timeit
|
|
99
106
|
@aws_handle_regions
|
|
100
|
-
def get_event_source_mappings(
|
|
107
|
+
def get_event_source_mappings(
|
|
108
|
+
lambda_function: Dict,
|
|
109
|
+
client: botocore.client.BaseClient,
|
|
110
|
+
) -> List[Any]:
|
|
101
111
|
event_source_mappings: List[Any] = []
|
|
102
|
-
paginator = client.get_paginator(
|
|
103
|
-
for page in paginator.paginate(FunctionName=lambda_function[
|
|
104
|
-
event_source_mappings.extend(page[
|
|
112
|
+
paginator = client.get_paginator("list_event_source_mappings")
|
|
113
|
+
for page in paginator.paginate(FunctionName=lambda_function["FunctionName"]):
|
|
114
|
+
event_source_mappings.extend(page["EventSourceMappings"])
|
|
105
115
|
|
|
106
116
|
return event_source_mappings
|
|
107
117
|
|
|
@@ -109,22 +119,32 @@ def get_event_source_mappings(lambda_function: Dict, client: botocore.client.Bas
|
|
|
109
119
|
@timeit
|
|
110
120
|
@aws_handle_regions
|
|
111
121
|
def get_lambda_function_details(
|
|
112
|
-
|
|
122
|
+
boto3_session: boto3.session.Session,
|
|
123
|
+
data: List[Dict],
|
|
124
|
+
region: str,
|
|
113
125
|
) -> List[Tuple[str, List[Any], List[Any], List[Any]]]:
|
|
114
|
-
client = boto3_session.client(
|
|
126
|
+
client = boto3_session.client("lambda", region_name=region)
|
|
115
127
|
details = []
|
|
116
128
|
for lambda_function in data:
|
|
117
129
|
function_aliases = get_function_aliases(lambda_function, client)
|
|
118
130
|
event_source_mappings = get_event_source_mappings(lambda_function, client)
|
|
119
|
-
layers = lambda_function.get(
|
|
120
|
-
details.append(
|
|
131
|
+
layers = lambda_function.get("Layers", [])
|
|
132
|
+
details.append(
|
|
133
|
+
(
|
|
134
|
+
lambda_function["FunctionArn"],
|
|
135
|
+
function_aliases,
|
|
136
|
+
event_source_mappings,
|
|
137
|
+
layers,
|
|
138
|
+
),
|
|
139
|
+
)
|
|
121
140
|
return details
|
|
122
141
|
|
|
123
142
|
|
|
124
143
|
@timeit
|
|
125
144
|
def load_lambda_function_details(
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
neo4j_session: neo4j.Session,
|
|
146
|
+
lambda_function_details: List[Tuple[str, List[Dict], List[Dict], List[Dict]]],
|
|
147
|
+
update_tag: int,
|
|
128
148
|
) -> None:
|
|
129
149
|
lambda_aliases: List[Dict] = []
|
|
130
150
|
lambda_event_source_mappings: List[Dict] = []
|
|
@@ -132,22 +152,30 @@ def load_lambda_function_details(
|
|
|
132
152
|
for function_arn, aliases, event_source_mappings, layers in lambda_function_details:
|
|
133
153
|
if len(aliases) > 0:
|
|
134
154
|
for alias in aliases:
|
|
135
|
-
alias[
|
|
155
|
+
alias["FunctionArn"] = function_arn
|
|
136
156
|
lambda_aliases.extend(aliases)
|
|
137
157
|
if len(event_source_mappings) > 0:
|
|
138
158
|
lambda_event_source_mappings.extend(event_source_mappings)
|
|
139
159
|
if len(layers) > 0:
|
|
140
160
|
for layer in layers:
|
|
141
|
-
layer[
|
|
161
|
+
layer["FunctionArn"] = function_arn
|
|
142
162
|
lambda_layers.extend(layers)
|
|
143
163
|
|
|
144
164
|
_load_lambda_function_aliases(neo4j_session, lambda_aliases, update_tag)
|
|
145
|
-
_load_lambda_event_source_mappings(
|
|
165
|
+
_load_lambda_event_source_mappings(
|
|
166
|
+
neo4j_session,
|
|
167
|
+
lambda_event_source_mappings,
|
|
168
|
+
update_tag,
|
|
169
|
+
)
|
|
146
170
|
_load_lambda_layers(neo4j_session, lambda_layers, update_tag)
|
|
147
171
|
|
|
148
172
|
|
|
149
173
|
@timeit
|
|
150
|
-
def _load_lambda_function_aliases(
|
|
174
|
+
def _load_lambda_function_aliases(
|
|
175
|
+
neo4j_session: neo4j.Session,
|
|
176
|
+
lambda_aliases: List[Dict],
|
|
177
|
+
update_tag: int,
|
|
178
|
+
) -> None:
|
|
151
179
|
ingest_aliases = """
|
|
152
180
|
UNWIND $aliases_list AS alias
|
|
153
181
|
MERGE (a:AWSLambdaFunctionAlias{id: alias.AliasArn})
|
|
@@ -173,7 +201,9 @@ def _load_lambda_function_aliases(neo4j_session: neo4j.Session, lambda_aliases:
|
|
|
173
201
|
|
|
174
202
|
@timeit
|
|
175
203
|
def _load_lambda_event_source_mappings(
|
|
176
|
-
|
|
204
|
+
neo4j_session: neo4j.Session,
|
|
205
|
+
lambda_event_source_mappings: List[Dict],
|
|
206
|
+
update_tag: int,
|
|
177
207
|
) -> None:
|
|
178
208
|
ingest_esms = """
|
|
179
209
|
UNWIND $esm_list AS esm
|
|
@@ -208,7 +238,11 @@ def _load_lambda_event_source_mappings(
|
|
|
208
238
|
|
|
209
239
|
|
|
210
240
|
@timeit
|
|
211
|
-
def _load_lambda_layers(
|
|
241
|
+
def _load_lambda_layers(
|
|
242
|
+
neo4j_session: neo4j.Session,
|
|
243
|
+
lambda_layers: List[Dict],
|
|
244
|
+
update_tag: int,
|
|
245
|
+
) -> None:
|
|
212
246
|
ingest_layers = """
|
|
213
247
|
UNWIND $layers_list AS layer
|
|
214
248
|
MERGE (l:AWSLambdaLayer{id: layer.Arn})
|
|
@@ -233,29 +267,64 @@ def _load_lambda_layers(neo4j_session: neo4j.Session, lambda_layers: List[Dict],
|
|
|
233
267
|
|
|
234
268
|
@timeit
|
|
235
269
|
def cleanup_lambda(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
|
|
236
|
-
run_cleanup_job(
|
|
270
|
+
run_cleanup_job(
|
|
271
|
+
"aws_import_lambda_cleanup.json",
|
|
272
|
+
neo4j_session,
|
|
273
|
+
common_job_parameters,
|
|
274
|
+
)
|
|
237
275
|
|
|
238
276
|
|
|
239
277
|
@timeit
|
|
240
278
|
def sync_lambda_functions(
|
|
241
|
-
|
|
242
|
-
|
|
279
|
+
neo4j_session: neo4j.Session,
|
|
280
|
+
boto3_session: boto3.session.Session,
|
|
281
|
+
regions: List[str],
|
|
282
|
+
current_aws_account_id: str,
|
|
283
|
+
aws_update_tag: int,
|
|
284
|
+
common_job_parameters: Dict,
|
|
243
285
|
) -> None:
|
|
244
286
|
for region in regions:
|
|
245
|
-
logger.info(
|
|
287
|
+
logger.info(
|
|
288
|
+
"Syncing Lambda for region in '%s' in account '%s'.",
|
|
289
|
+
region,
|
|
290
|
+
current_aws_account_id,
|
|
291
|
+
)
|
|
246
292
|
data = get_lambda_data(boto3_session, region)
|
|
247
|
-
load_lambda_functions(
|
|
248
|
-
|
|
249
|
-
|
|
293
|
+
load_lambda_functions(
|
|
294
|
+
neo4j_session,
|
|
295
|
+
data,
|
|
296
|
+
region,
|
|
297
|
+
current_aws_account_id,
|
|
298
|
+
aws_update_tag,
|
|
299
|
+
)
|
|
300
|
+
lambda_function_details = get_lambda_function_details(
|
|
301
|
+
boto3_session,
|
|
302
|
+
data,
|
|
303
|
+
region,
|
|
304
|
+
)
|
|
305
|
+
load_lambda_function_details(
|
|
306
|
+
neo4j_session,
|
|
307
|
+
lambda_function_details,
|
|
308
|
+
aws_update_tag,
|
|
309
|
+
)
|
|
250
310
|
|
|
251
311
|
cleanup_lambda(neo4j_session, common_job_parameters)
|
|
252
312
|
|
|
253
313
|
|
|
254
314
|
@timeit
|
|
255
315
|
def sync(
|
|
256
|
-
|
|
257
|
-
|
|
316
|
+
neo4j_session: neo4j.Session,
|
|
317
|
+
boto3_session: boto3.session.Session,
|
|
318
|
+
regions: List[str],
|
|
319
|
+
current_aws_account_id: str,
|
|
320
|
+
update_tag: int,
|
|
321
|
+
common_job_parameters: Dict,
|
|
258
322
|
) -> None:
|
|
259
323
|
sync_lambda_functions(
|
|
260
|
-
neo4j_session,
|
|
324
|
+
neo4j_session,
|
|
325
|
+
boto3_session,
|
|
326
|
+
regions,
|
|
327
|
+
current_aws_account_id,
|
|
328
|
+
update_tag,
|
|
329
|
+
common_job_parameters,
|
|
261
330
|
)
|
|
@@ -16,19 +16,22 @@ def get_account_from_arn(arn: str) -> str:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def get_caller_identity(boto3_session: boto3.session.Session) -> Dict:
|
|
19
|
-
client = boto3_session.client(
|
|
19
|
+
client = boto3_session.client("sts")
|
|
20
20
|
return client.get_caller_identity()
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def get_current_aws_account_id(boto3_session: boto3.session.Session) -> Dict:
|
|
24
|
-
return get_caller_identity(boto3_session)[
|
|
24
|
+
return get_caller_identity(boto3_session)["Account"]
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def get_aws_account_default(boto3_session: boto3.session.Session) -> Dict:
|
|
28
28
|
try:
|
|
29
29
|
return {boto3_session.profile_name: get_current_aws_account_id(boto3_session)}
|
|
30
30
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
|
31
|
-
logger.debug(
|
|
31
|
+
logger.debug(
|
|
32
|
+
"Error occurred getting default AWS account number.",
|
|
33
|
+
exc_info=True,
|
|
34
|
+
)
|
|
32
35
|
logger.error(
|
|
33
36
|
(
|
|
34
37
|
"Unable to get AWS account number, an error occurred: '%s'. Make sure your AWS credentials are "
|
|
@@ -43,13 +46,20 @@ def get_aws_account_default(boto3_session: boto3.session.Session) -> Dict:
|
|
|
43
46
|
def get_aws_accounts_from_botocore_config(boto3_session: boto3.session.Session) -> Dict:
|
|
44
47
|
d = {}
|
|
45
48
|
for profile_name in boto3_session.available_profiles:
|
|
46
|
-
if profile_name ==
|
|
49
|
+
if profile_name == "default":
|
|
47
50
|
logger.debug("Skipping AWS profile 'default'.")
|
|
48
51
|
continue
|
|
49
52
|
try:
|
|
50
53
|
profile_boto3_session = boto3.Session(profile_name=profile_name)
|
|
51
|
-
except (
|
|
52
|
-
|
|
54
|
+
except (
|
|
55
|
+
botocore.exceptions.BotoCoreError,
|
|
56
|
+
botocore.exceptions.ClientError,
|
|
57
|
+
) as e:
|
|
58
|
+
logger.debug(
|
|
59
|
+
"Error occurred calling boto3.Session() with profile_name '%s'.",
|
|
60
|
+
profile_name,
|
|
61
|
+
exc_info=True,
|
|
62
|
+
)
|
|
53
63
|
logger.error(
|
|
54
64
|
(
|
|
55
65
|
"Unable to initialize an AWS session using profile '%s', an error occurred: '%s'. Make sure your "
|
|
@@ -62,7 +72,10 @@ def get_aws_accounts_from_botocore_config(boto3_session: boto3.session.Session)
|
|
|
62
72
|
continue
|
|
63
73
|
try:
|
|
64
74
|
d[profile_name] = get_current_aws_account_id(profile_boto3_session)
|
|
65
|
-
except (
|
|
75
|
+
except (
|
|
76
|
+
botocore.exceptions.BotoCoreError,
|
|
77
|
+
botocore.exceptions.ClientError,
|
|
78
|
+
) as e:
|
|
66
79
|
logger.debug(
|
|
67
80
|
"Error occurred getting AWS account number with profile_name '%s'.",
|
|
68
81
|
profile_name,
|
|
@@ -87,7 +100,9 @@ def get_aws_accounts_from_botocore_config(boto3_session: boto3.session.Session)
|
|
|
87
100
|
|
|
88
101
|
|
|
89
102
|
def load_aws_accounts(
|
|
90
|
-
neo4j_session: neo4j.Session,
|
|
103
|
+
neo4j_session: neo4j.Session,
|
|
104
|
+
aws_accounts: Dict,
|
|
105
|
+
aws_update_tag: int,
|
|
91
106
|
common_job_parameters: Dict,
|
|
92
107
|
) -> None:
|
|
93
108
|
query = """
|
|
@@ -105,7 +120,7 @@ def load_aws_accounts(
|
|
|
105
120
|
SET r.lastupdated = $aws_update_tag;
|
|
106
121
|
"""
|
|
107
122
|
for account_name, account_id in aws_accounts.items():
|
|
108
|
-
root_arn = f
|
|
123
|
+
root_arn = f"arn:aws:iam::{account_id}:root"
|
|
109
124
|
neo4j_session.run(
|
|
110
125
|
query,
|
|
111
126
|
ACCOUNT_ID=account_id,
|
|
@@ -116,5 +131,10 @@ def load_aws_accounts(
|
|
|
116
131
|
|
|
117
132
|
|
|
118
133
|
@timeit
|
|
119
|
-
def sync(
|
|
134
|
+
def sync(
|
|
135
|
+
neo4j_session: neo4j.Session,
|
|
136
|
+
accounts: Dict,
|
|
137
|
+
update_tag: int,
|
|
138
|
+
common_job_parameters: Dict,
|
|
139
|
+
) -> None:
|
|
120
140
|
load_aws_accounts(neo4j_session, accounts, update_tag, common_job_parameters)
|