cartography 0.109.0rc1__py3-none-any.whl → 0.109.0rc2__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/_version.py +2 -2
- cartography/data/indexes.cypher +0 -15
- cartography/intel/aws/glue.py +117 -0
- cartography/intel/aws/identitycenter.py +71 -23
- cartography/intel/aws/kms.py +160 -200
- cartography/intel/aws/lambda_function.py +206 -190
- cartography/intel/aws/rds.py +243 -458
- cartography/intel/aws/resources.py +2 -0
- cartography/intel/aws/route53.py +334 -332
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/identitycenter/awspermissionset.py +44 -0
- 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 +89 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +89 -0
- cartography/models/aws/rds/instance.py +154 -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 +214 -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/util.py +8 -1
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/METADATA +2 -2
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/RECORD +38 -23
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_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_kms_details.json +0 -10
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/WHEEL +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/entry_points.txt +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.109.0rc2.dist-info}/top_level.txt +0 -0
cartography/_version.py
CHANGED
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.109.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 109, 0, '
|
|
20
|
+
__version__ = version = '0.109.0rc2'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 109, 0, 'rc2')
|
cartography/data/indexes.cypher
CHANGED
|
@@ -29,14 +29,6 @@ CREATE INDEX IF NOT EXISTS FOR (n:AWSIpv4CidrBlock) ON (n.id);
|
|
|
29
29
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSIpv4CidrBlock) ON (n.lastupdated);
|
|
30
30
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSIpv6CidrBlock) ON (n.id);
|
|
31
31
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSIpv6CidrBlock) ON (n.lastupdated);
|
|
32
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambda) ON (n.id);
|
|
33
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambda) ON (n.lastupdated);
|
|
34
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaEventSourceMapping) ON (n.id);
|
|
35
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaEventSourceMapping) ON (n.lastupdated);
|
|
36
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaFunctionAlias) ON (n.id);
|
|
37
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaFunctionAlias) ON (n.lastupdated);
|
|
38
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaLayer) ON (n.id);
|
|
39
|
-
CREATE INDEX IF NOT EXISTS FOR (n:AWSLambdaLayer) ON (n.lastupdated);
|
|
40
32
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSPeeringConnection) ON (n.id);
|
|
41
33
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSPeeringConnection) ON (n.lastupdated);
|
|
42
34
|
CREATE INDEX IF NOT EXISTS FOR (n:AWSPolicy) ON (n.id);
|
|
@@ -158,13 +150,6 @@ CREATE INDEX IF NOT EXISTS FOR (n:IpRange) ON (n.id);
|
|
|
158
150
|
CREATE INDEX IF NOT EXISTS FOR (n:IpRange) ON (n.lastupdated);
|
|
159
151
|
CREATE INDEX IF NOT EXISTS FOR (n:JamfComputerGroup) ON (n.id);
|
|
160
152
|
CREATE INDEX IF NOT EXISTS FOR (n:JamfComputerGroup) ON (n.lastupdated);
|
|
161
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSKey) ON (n.id);
|
|
162
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSKey) ON (n.arn);
|
|
163
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSKey) ON (n.lastupdated);
|
|
164
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSAlias) ON (n.id);
|
|
165
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSAlias) ON (n.lastupdated);
|
|
166
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSGrant) ON (n.id);
|
|
167
|
-
CREATE INDEX IF NOT EXISTS FOR (n:KMSGrant) ON (n.lastupdated);
|
|
168
153
|
CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.id);
|
|
169
154
|
CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.name);
|
|
170
155
|
CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.lastupdated);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
import neo4j
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.aws.ec2.util import get_botocore_config
|
|
12
|
+
from cartography.models.aws.glue.connection import GlueConnectionSchema
|
|
13
|
+
from cartography.util import aws_handle_regions
|
|
14
|
+
from cartography.util import timeit
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
@aws_handle_regions
|
|
21
|
+
def get_glue_connections(
|
|
22
|
+
boto3_session: boto3.Session, region: str
|
|
23
|
+
) -> List[Dict[str, Any]]:
|
|
24
|
+
client = boto3_session.client(
|
|
25
|
+
"glue", region_name=region, config=get_botocore_config()
|
|
26
|
+
)
|
|
27
|
+
paginator = client.get_paginator("get_connections")
|
|
28
|
+
connections = []
|
|
29
|
+
for page in paginator.paginate():
|
|
30
|
+
connections.extend(page.get("ConnectionList", []))
|
|
31
|
+
|
|
32
|
+
return connections
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def transform_glue_connections(
|
|
36
|
+
connections: List[Dict[str, Any]], region: str
|
|
37
|
+
) -> List[Dict[str, Any]]:
|
|
38
|
+
"""
|
|
39
|
+
Transform Glue connection data for ingestion
|
|
40
|
+
"""
|
|
41
|
+
transformed_connections = []
|
|
42
|
+
for connection in connections:
|
|
43
|
+
transformed_connection = {
|
|
44
|
+
"Name": connection["Name"],
|
|
45
|
+
"Description": connection.get("Description"),
|
|
46
|
+
"ConnectionType": connection.get("ConnectionType"),
|
|
47
|
+
"Status": connection.get("Status"),
|
|
48
|
+
"StatusReason": connection.get("StatusReason"),
|
|
49
|
+
"AuthenticationType": connection.get("AuthenticationConfiguration", {}).get(
|
|
50
|
+
"AuthenticationType"
|
|
51
|
+
),
|
|
52
|
+
"SecretArn": connection.get("AuthenticationConfiguration", {}).get(
|
|
53
|
+
"SecretArn"
|
|
54
|
+
),
|
|
55
|
+
"Region": region,
|
|
56
|
+
}
|
|
57
|
+
transformed_connections.append(transformed_connection)
|
|
58
|
+
return transformed_connections
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@timeit
|
|
62
|
+
def load_glue_connections(
|
|
63
|
+
neo4j_session: neo4j.Session,
|
|
64
|
+
data: List[Dict[str, Any]],
|
|
65
|
+
region: str,
|
|
66
|
+
current_aws_account_id: str,
|
|
67
|
+
aws_update_tag: int,
|
|
68
|
+
) -> None:
|
|
69
|
+
logger.info(
|
|
70
|
+
f"Loading Glue {len(data)} connections for region '{region}' into graph.",
|
|
71
|
+
)
|
|
72
|
+
load(
|
|
73
|
+
neo4j_session,
|
|
74
|
+
GlueConnectionSchema(),
|
|
75
|
+
data,
|
|
76
|
+
lastupdated=aws_update_tag,
|
|
77
|
+
Region=region,
|
|
78
|
+
AWS_ID=current_aws_account_id,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@timeit
|
|
83
|
+
def cleanup(
|
|
84
|
+
neo4j_session: neo4j.Session,
|
|
85
|
+
common_job_parameters: Dict[str, Any],
|
|
86
|
+
) -> None:
|
|
87
|
+
logger.debug("Running Glue cleanup job.")
|
|
88
|
+
GraphJob.from_node_schema(GlueConnectionSchema(), common_job_parameters).run(
|
|
89
|
+
neo4j_session
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@timeit
|
|
94
|
+
def sync(
|
|
95
|
+
neo4j_session: neo4j.Session,
|
|
96
|
+
boto3_session: boto3.session.Session,
|
|
97
|
+
regions: List[str],
|
|
98
|
+
current_aws_account_id: str,
|
|
99
|
+
update_tag: int,
|
|
100
|
+
common_job_parameters: Dict[str, Any],
|
|
101
|
+
) -> None:
|
|
102
|
+
for region in regions:
|
|
103
|
+
logger.info(
|
|
104
|
+
f"Syncing Glue for region '{region}' in account '{current_aws_account_id}'.",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
connections = get_glue_connections(boto3_session, region)
|
|
108
|
+
transformed_connections = transform_glue_connections(connections, region)
|
|
109
|
+
load_glue_connections(
|
|
110
|
+
neo4j_session,
|
|
111
|
+
transformed_connections,
|
|
112
|
+
region,
|
|
113
|
+
current_aws_account_id,
|
|
114
|
+
update_tag,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
@@ -7,6 +7,7 @@ import boto3
|
|
|
7
7
|
import neo4j
|
|
8
8
|
|
|
9
9
|
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.client.core.tx import load_matchlinks
|
|
10
11
|
from cartography.graph.job import GraphJob
|
|
11
12
|
from cartography.models.aws.identitycenter.awsidentitycenter import (
|
|
12
13
|
AWSIdentityCenterInstanceSchema,
|
|
@@ -14,9 +15,11 @@ from cartography.models.aws.identitycenter.awsidentitycenter import (
|
|
|
14
15
|
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
15
16
|
AWSPermissionSetSchema,
|
|
16
17
|
)
|
|
18
|
+
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
19
|
+
RoleAssignmentAllowedByMatchLink,
|
|
20
|
+
)
|
|
17
21
|
from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
|
|
18
22
|
from cartography.util import aws_handle_regions
|
|
19
|
-
from cartography.util import run_cleanup_job
|
|
20
23
|
from cartography.util import timeit
|
|
21
24
|
|
|
22
25
|
logger = logging.getLogger(__name__)
|
|
@@ -120,6 +123,8 @@ def load_permission_sets(
|
|
|
120
123
|
InstanceArn=instance_arn,
|
|
121
124
|
Region=region,
|
|
122
125
|
AWS_ID=aws_account_id,
|
|
126
|
+
_sub_resource_label="AWSAccount",
|
|
127
|
+
_sub_resource_id=aws_account_id,
|
|
123
128
|
)
|
|
124
129
|
|
|
125
130
|
|
|
@@ -220,31 +225,64 @@ def get_role_assignments(
|
|
|
220
225
|
return role_assignments
|
|
221
226
|
|
|
222
227
|
|
|
228
|
+
@timeit
|
|
229
|
+
def get_permset_roles(
|
|
230
|
+
neo4j_session: neo4j.Session,
|
|
231
|
+
role_assignments: List[Dict[str, Any]],
|
|
232
|
+
) -> List[Dict[str, Any]]:
|
|
233
|
+
"""
|
|
234
|
+
Enrich role assignments with exact role ARNs by querying existing permission set relationships.
|
|
235
|
+
Uses the ASSIGNED_TO_ROLE relationships created when permission sets were loaded.
|
|
236
|
+
"""
|
|
237
|
+
# Get unique permission set ARNs from role assignments
|
|
238
|
+
permset_ids = list({ra["PermissionSetArn"] for ra in role_assignments})
|
|
239
|
+
|
|
240
|
+
query = """
|
|
241
|
+
MATCH (role:AWSRole)<-[:ASSIGNED_TO_ROLE]-(permset:AWSPermissionSet)
|
|
242
|
+
WHERE permset.arn IN $PermSetIds
|
|
243
|
+
RETURN permset.arn AS PermissionSetArn, role.arn AS RoleArn
|
|
244
|
+
"""
|
|
245
|
+
result = neo4j_session.run(query, PermSetIds=permset_ids)
|
|
246
|
+
permset_to_role = [record.data() for record in result]
|
|
247
|
+
|
|
248
|
+
# Create mapping from permission set ARN to role ARN
|
|
249
|
+
permset_to_role_map = {
|
|
250
|
+
entry["PermissionSetArn"]: entry["RoleArn"] for entry in permset_to_role
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Enrich role assignments with exact role ARNs
|
|
254
|
+
enriched_assignments = []
|
|
255
|
+
for assignment in role_assignments:
|
|
256
|
+
role_arn = permset_to_role_map.get(assignment["PermissionSetArn"])
|
|
257
|
+
enriched_assignments.append(
|
|
258
|
+
{
|
|
259
|
+
**assignment,
|
|
260
|
+
"RoleArn": role_arn,
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return enriched_assignments
|
|
265
|
+
|
|
266
|
+
|
|
223
267
|
@timeit
|
|
224
268
|
def load_role_assignments(
|
|
225
269
|
neo4j_session: neo4j.Session,
|
|
226
270
|
role_assignments: List[Dict],
|
|
271
|
+
aws_account_id: str,
|
|
227
272
|
aws_update_tag: int,
|
|
228
273
|
) -> None:
|
|
229
274
|
"""
|
|
230
|
-
Load role assignments into the graph
|
|
275
|
+
Load role assignments into the graph using MatchLink schema
|
|
231
276
|
"""
|
|
232
277
|
logger.info(f"Loading {len(role_assignments)} role assignments")
|
|
233
|
-
|
|
234
|
-
neo4j_session
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
MERGE (role)-[r:ALLOWED_BY]->(sso)
|
|
242
|
-
SET r.lastupdated = $aws_update_tag,
|
|
243
|
-
r.permission_set_arn = ra.PermissionSetArn
|
|
244
|
-
""",
|
|
245
|
-
role_assignments=role_assignments,
|
|
246
|
-
aws_update_tag=aws_update_tag,
|
|
247
|
-
)
|
|
278
|
+
load_matchlinks(
|
|
279
|
+
neo4j_session,
|
|
280
|
+
RoleAssignmentAllowedByMatchLink(),
|
|
281
|
+
role_assignments,
|
|
282
|
+
lastupdated=aws_update_tag,
|
|
283
|
+
_sub_resource_label="AWSAccount",
|
|
284
|
+
_sub_resource_id=aws_account_id,
|
|
285
|
+
)
|
|
248
286
|
|
|
249
287
|
|
|
250
288
|
@timeit
|
|
@@ -262,11 +300,14 @@ def cleanup(
|
|
|
262
300
|
GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(
|
|
263
301
|
neo4j_session,
|
|
264
302
|
)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
303
|
+
|
|
304
|
+
# Clean up role assignment MatchLinks
|
|
305
|
+
GraphJob.from_matchlink(
|
|
306
|
+
RoleAssignmentAllowedByMatchLink(),
|
|
307
|
+
"AWSAccount",
|
|
308
|
+
common_job_parameters["AWS_ID"],
|
|
309
|
+
common_job_parameters["UPDATE_TAG"],
|
|
310
|
+
).run(neo4j_session)
|
|
270
311
|
|
|
271
312
|
|
|
272
313
|
@timeit
|
|
@@ -327,9 +368,16 @@ def sync_identity_center_instances(
|
|
|
327
368
|
instance_arn,
|
|
328
369
|
region,
|
|
329
370
|
)
|
|
330
|
-
|
|
371
|
+
|
|
372
|
+
# Enrich role assignments with exact role ARNs using permission set relationships
|
|
373
|
+
enriched_role_assignments = get_permset_roles(
|
|
331
374
|
neo4j_session,
|
|
332
375
|
role_assignments,
|
|
376
|
+
)
|
|
377
|
+
load_role_assignments(
|
|
378
|
+
neo4j_session,
|
|
379
|
+
enriched_role_assignments,
|
|
380
|
+
current_aws_account_id,
|
|
333
381
|
update_tag,
|
|
334
382
|
)
|
|
335
383
|
|