cartography 0.110.0rc1__py3-none-any.whl → 0.110.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/cli.py +0 -8
- cartography/config.py +0 -9
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/ecs.py +7 -1
- cartography/intel/aws/glue.py +64 -0
- cartography/intel/aws/kms.py +13 -1
- cartography/intel/aws/rds.py +105 -0
- cartography/intel/aws/resources.py +2 -0
- cartography/intel/aws/route53.py +3 -1
- cartography/intel/aws/s3.py +104 -0
- cartography/intel/entra/__init__.py +41 -43
- cartography/intel/entra/applications.py +2 -1
- cartography/intel/entra/ou.py +1 -1
- cartography/intel/github/__init__.py +21 -25
- cartography/intel/github/repos.py +4 -36
- cartography/intel/kubernetes/__init__.py +4 -0
- cartography/intel/kubernetes/rbac.py +464 -0
- cartography/intel/kubernetes/util.py +17 -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/ec2/security_groups.py +1 -1
- cartography/models/aws/ecs/services.py +17 -0
- cartography/models/aws/ecs/tasks.py +1 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/route53/dnsrecord.py +21 -0
- cartography/models/github/dependencies.py +1 -2
- cartography/models/kubernetes/clusterrolebindings.py +98 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/rolebindings.py +119 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/METADATA +3 -3
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/RECORD +42 -31
- cartography/intel/entra/resources.py +0 -20
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/WHEEL +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/entry_points.txt +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.110.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from kubernetes.client import V1ClusterRole
|
|
8
|
+
from kubernetes.client import V1ClusterRoleBinding
|
|
9
|
+
from kubernetes.client import V1Role
|
|
10
|
+
from kubernetes.client import V1RoleBinding
|
|
11
|
+
from kubernetes.client import V1ServiceAccount
|
|
12
|
+
|
|
13
|
+
from cartography.client.core.tx import load
|
|
14
|
+
from cartography.graph.job import GraphJob
|
|
15
|
+
from cartography.intel.kubernetes.util import get_epoch
|
|
16
|
+
from cartography.intel.kubernetes.util import k8s_paginate
|
|
17
|
+
from cartography.intel.kubernetes.util import K8sClient
|
|
18
|
+
from cartography.models.kubernetes.clusterrolebindings import (
|
|
19
|
+
KubernetesClusterRoleBindingSchema,
|
|
20
|
+
)
|
|
21
|
+
from cartography.models.kubernetes.clusterroles import KubernetesClusterRoleSchema
|
|
22
|
+
from cartography.models.kubernetes.rolebindings import KubernetesRoleBindingSchema
|
|
23
|
+
from cartography.models.kubernetes.roles import KubernetesRoleSchema
|
|
24
|
+
from cartography.models.kubernetes.serviceaccounts import KubernetesServiceAccountSchema
|
|
25
|
+
from cartography.util import timeit
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@timeit
|
|
31
|
+
def get_service_accounts(k8s_client: K8sClient) -> List[V1ServiceAccount]:
|
|
32
|
+
|
|
33
|
+
return k8s_paginate(k8s_client.core.list_service_account_for_all_namespaces)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@timeit
|
|
37
|
+
def get_roles(k8s_client: K8sClient) -> List[V1Role]:
|
|
38
|
+
|
|
39
|
+
return k8s_paginate(k8s_client.rbac.list_role_for_all_namespaces)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@timeit
|
|
43
|
+
def get_role_bindings(k8s_client: K8sClient) -> List[V1RoleBinding]:
|
|
44
|
+
|
|
45
|
+
return k8s_paginate(k8s_client.rbac.list_role_binding_for_all_namespaces)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
def get_cluster_roles(k8s_client: K8sClient) -> List[V1ClusterRole]:
|
|
50
|
+
|
|
51
|
+
return k8s_paginate(k8s_client.rbac.list_cluster_role)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@timeit
|
|
55
|
+
def get_cluster_role_bindings(k8s_client: K8sClient) -> List[V1ClusterRoleBinding]:
|
|
56
|
+
|
|
57
|
+
return k8s_paginate(k8s_client.rbac.list_cluster_role_binding)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def transform_service_accounts(
|
|
61
|
+
service_accounts: List[V1ServiceAccount], cluster_name: str
|
|
62
|
+
) -> List[Dict[str, Any]]:
|
|
63
|
+
"""
|
|
64
|
+
Transform Kubernetes ServiceAccounts into a list of dictionaries.
|
|
65
|
+
Uses cluster-scoped IDs to prevent collisions across multiple clusters.
|
|
66
|
+
"""
|
|
67
|
+
result = []
|
|
68
|
+
for sa in service_accounts:
|
|
69
|
+
result.append(
|
|
70
|
+
{
|
|
71
|
+
"id": f"{cluster_name}/{sa.metadata.namespace}/{sa.metadata.name}",
|
|
72
|
+
"name": sa.metadata.name,
|
|
73
|
+
"namespace": sa.metadata.namespace,
|
|
74
|
+
"uid": sa.metadata.uid,
|
|
75
|
+
"creation_timestamp": get_epoch(sa.metadata.creation_timestamp),
|
|
76
|
+
"resource_version": sa.metadata.resource_version,
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def transform_roles(roles: List[V1Role], cluster_name: str) -> List[Dict[str, Any]]:
|
|
83
|
+
"""
|
|
84
|
+
Transform Kubernetes Roles into a list of dictionaries.
|
|
85
|
+
Flattens rules into separate api_groups, resources, and verbs lists.
|
|
86
|
+
"""
|
|
87
|
+
result = []
|
|
88
|
+
for role in roles:
|
|
89
|
+
# Flatten all rules into combined sets
|
|
90
|
+
all_api_groups: set[str] = set()
|
|
91
|
+
all_resources: set[str] = set()
|
|
92
|
+
all_verbs: set[str] = set()
|
|
93
|
+
|
|
94
|
+
for rule in role.rules or []:
|
|
95
|
+
# Update api_groups, handling None and empty string cases
|
|
96
|
+
all_api_groups.update(
|
|
97
|
+
{
|
|
98
|
+
"core" if api_group == "" else api_group
|
|
99
|
+
for api_group in rule.api_groups or []
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
all_resources.update(rule.resources or [])
|
|
103
|
+
all_verbs.update(rule.verbs or [])
|
|
104
|
+
|
|
105
|
+
result.append(
|
|
106
|
+
{
|
|
107
|
+
"id": f"{cluster_name}/{role.metadata.namespace}/{role.metadata.name}",
|
|
108
|
+
"name": role.metadata.name,
|
|
109
|
+
"namespace": role.metadata.namespace,
|
|
110
|
+
"uid": role.metadata.uid,
|
|
111
|
+
"creation_timestamp": get_epoch(role.metadata.creation_timestamp),
|
|
112
|
+
"resource_version": role.metadata.resource_version,
|
|
113
|
+
"api_groups": sorted(
|
|
114
|
+
all_api_groups
|
|
115
|
+
), # sorts to keep consistent ordering and converts to list to appease neo4j
|
|
116
|
+
"resources": sorted(all_resources),
|
|
117
|
+
"verbs": sorted(all_verbs),
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def transform_role_bindings(
|
|
124
|
+
role_bindings: List[V1RoleBinding], cluster_name: str
|
|
125
|
+
) -> List[Dict[str, Any]]:
|
|
126
|
+
"""
|
|
127
|
+
Transform Kubernetes RoleBindings into a list of dictionaries.
|
|
128
|
+
Creates one RoleBinding node per Kubernetes RoleBinding with lists of subject IDs.
|
|
129
|
+
"""
|
|
130
|
+
result = []
|
|
131
|
+
for rb in role_bindings:
|
|
132
|
+
# Collect all subjects by type
|
|
133
|
+
service_account_subjects = [
|
|
134
|
+
subject
|
|
135
|
+
for subject in (rb.subjects or [])
|
|
136
|
+
if subject.kind == "ServiceAccount"
|
|
137
|
+
]
|
|
138
|
+
user_subjects = [
|
|
139
|
+
subject for subject in (rb.subjects or []) if subject.kind == "User"
|
|
140
|
+
]
|
|
141
|
+
group_subjects = [
|
|
142
|
+
subject for subject in (rb.subjects or []) if subject.kind == "Group"
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
# Only create a RoleBinding node if it has at least one subject
|
|
146
|
+
if rb.subjects:
|
|
147
|
+
result.append(
|
|
148
|
+
{
|
|
149
|
+
"id": f"{cluster_name}/{rb.metadata.namespace}/{rb.metadata.name}",
|
|
150
|
+
"name": rb.metadata.name,
|
|
151
|
+
"namespace": rb.metadata.namespace,
|
|
152
|
+
"uid": rb.metadata.uid,
|
|
153
|
+
"creation_timestamp": get_epoch(rb.metadata.creation_timestamp),
|
|
154
|
+
"resource_version": rb.metadata.resource_version,
|
|
155
|
+
"role_name": rb.role_ref.name,
|
|
156
|
+
"role_kind": rb.role_ref.kind,
|
|
157
|
+
"service_account_ids": [
|
|
158
|
+
f"{cluster_name}/{subject.namespace}/{subject.name}"
|
|
159
|
+
for subject in service_account_subjects
|
|
160
|
+
],
|
|
161
|
+
"user_ids": [
|
|
162
|
+
f"{cluster_name}/{subject.name}" for subject in user_subjects
|
|
163
|
+
],
|
|
164
|
+
"group_ids": [
|
|
165
|
+
f"{cluster_name}/{subject.name}" for subject in group_subjects
|
|
166
|
+
],
|
|
167
|
+
"role_id": f"{cluster_name}/{rb.metadata.namespace}/{rb.role_ref.name}",
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
return result
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def transform_cluster_roles(
|
|
174
|
+
cluster_roles: List[V1ClusterRole], cluster_name: str
|
|
175
|
+
) -> List[Dict[str, Any]]:
|
|
176
|
+
"""
|
|
177
|
+
Transform Kubernetes ClusterRoles into a list of dictionaries.
|
|
178
|
+
Flattens rules into separate api_groups, resources, and verbs lists.
|
|
179
|
+
"""
|
|
180
|
+
result = []
|
|
181
|
+
for cluster_role in cluster_roles:
|
|
182
|
+
# Flatten all rules into combined sets
|
|
183
|
+
all_api_groups: set[str] = set()
|
|
184
|
+
all_resources: set[str] = set()
|
|
185
|
+
all_verbs: set[str] = set()
|
|
186
|
+
|
|
187
|
+
for rule in cluster_role.rules or []:
|
|
188
|
+
# Update api_groups, handling None and empty string cases
|
|
189
|
+
all_api_groups.update(
|
|
190
|
+
{
|
|
191
|
+
"core" if api_group == "" else api_group
|
|
192
|
+
for api_group in rule.api_groups or []
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
all_resources.update(rule.resources or [])
|
|
196
|
+
all_verbs.update(rule.verbs or [])
|
|
197
|
+
|
|
198
|
+
result.append(
|
|
199
|
+
{
|
|
200
|
+
"id": f"{cluster_name}/{cluster_role.metadata.name}",
|
|
201
|
+
"name": cluster_role.metadata.name,
|
|
202
|
+
"uid": cluster_role.metadata.uid,
|
|
203
|
+
"creation_timestamp": get_epoch(
|
|
204
|
+
cluster_role.metadata.creation_timestamp
|
|
205
|
+
),
|
|
206
|
+
"resource_version": cluster_role.metadata.resource_version,
|
|
207
|
+
"api_groups": sorted(
|
|
208
|
+
all_api_groups
|
|
209
|
+
), # sorts to keep consistent ordering and converts to list to appease neo4j
|
|
210
|
+
"resources": sorted(all_resources),
|
|
211
|
+
"verbs": sorted(all_verbs),
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
return result
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def transform_cluster_role_bindings(
|
|
218
|
+
cluster_role_bindings: List[V1ClusterRoleBinding], cluster_name: str
|
|
219
|
+
) -> List[Dict[str, Any]]:
|
|
220
|
+
"""
|
|
221
|
+
Transform Kubernetes ClusterRoleBindings into a list of dictionaries.
|
|
222
|
+
Creates one ClusterRoleBinding node per Kubernetes ClusterRoleBinding with lists of subject IDs.
|
|
223
|
+
"""
|
|
224
|
+
result = []
|
|
225
|
+
for crb in cluster_role_bindings:
|
|
226
|
+
# Collect all subjects by type
|
|
227
|
+
service_account_subjects = [
|
|
228
|
+
subject
|
|
229
|
+
for subject in (crb.subjects or [])
|
|
230
|
+
if subject.kind == "ServiceAccount"
|
|
231
|
+
]
|
|
232
|
+
user_subjects = [
|
|
233
|
+
subject for subject in (crb.subjects or []) if subject.kind == "User"
|
|
234
|
+
]
|
|
235
|
+
group_subjects = [
|
|
236
|
+
subject for subject in (crb.subjects or []) if subject.kind == "Group"
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
# Only create a ClusterRoleBinding node if it has at least one subject
|
|
240
|
+
if crb.subjects:
|
|
241
|
+
result.append(
|
|
242
|
+
{
|
|
243
|
+
"id": f"{cluster_name}/{crb.metadata.name}",
|
|
244
|
+
"name": crb.metadata.name,
|
|
245
|
+
"uid": crb.metadata.uid,
|
|
246
|
+
"creation_timestamp": get_epoch(crb.metadata.creation_timestamp),
|
|
247
|
+
"resource_version": crb.metadata.resource_version,
|
|
248
|
+
"role_name": crb.role_ref.name,
|
|
249
|
+
"role_kind": crb.role_ref.kind,
|
|
250
|
+
"service_account_ids": [
|
|
251
|
+
f"{cluster_name}/{subject.namespace}/{subject.name}"
|
|
252
|
+
for subject in service_account_subjects
|
|
253
|
+
],
|
|
254
|
+
"user_ids": [
|
|
255
|
+
f"{cluster_name}/{subject.name}" for subject in user_subjects
|
|
256
|
+
],
|
|
257
|
+
"group_ids": [
|
|
258
|
+
f"{cluster_name}/{subject.name}" for subject in group_subjects
|
|
259
|
+
],
|
|
260
|
+
"role_id": f"{cluster_name}/{crb.role_ref.name}",
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
return result
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@timeit
|
|
267
|
+
def load_service_accounts(
|
|
268
|
+
session: neo4j.Session,
|
|
269
|
+
service_accounts: List[Dict[str, Any]],
|
|
270
|
+
update_tag: int,
|
|
271
|
+
cluster_id: str,
|
|
272
|
+
cluster_name: str,
|
|
273
|
+
) -> None:
|
|
274
|
+
logger.info(f"Loading {len(service_accounts)} KubernetesServiceAccounts")
|
|
275
|
+
load(
|
|
276
|
+
session,
|
|
277
|
+
KubernetesServiceAccountSchema(),
|
|
278
|
+
service_accounts,
|
|
279
|
+
lastupdated=update_tag,
|
|
280
|
+
CLUSTER_ID=cluster_id,
|
|
281
|
+
CLUSTER_NAME=cluster_name,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@timeit
|
|
286
|
+
def load_roles(
|
|
287
|
+
session: neo4j.Session,
|
|
288
|
+
roles: List[Dict[str, Any]],
|
|
289
|
+
update_tag: int,
|
|
290
|
+
cluster_id: str,
|
|
291
|
+
cluster_name: str,
|
|
292
|
+
) -> None:
|
|
293
|
+
logger.info(f"Loading {len(roles)} KubernetesRoles")
|
|
294
|
+
load(
|
|
295
|
+
session,
|
|
296
|
+
KubernetesRoleSchema(),
|
|
297
|
+
roles,
|
|
298
|
+
lastupdated=update_tag,
|
|
299
|
+
CLUSTER_ID=cluster_id,
|
|
300
|
+
CLUSTER_NAME=cluster_name,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@timeit
|
|
305
|
+
def load_role_bindings(
|
|
306
|
+
session: neo4j.Session,
|
|
307
|
+
role_bindings: List[Dict[str, Any]],
|
|
308
|
+
update_tag: int,
|
|
309
|
+
cluster_id: str,
|
|
310
|
+
cluster_name: str,
|
|
311
|
+
) -> None:
|
|
312
|
+
logger.info(f"Loading {len(role_bindings)} KubernetesRoleBindings")
|
|
313
|
+
load(
|
|
314
|
+
session,
|
|
315
|
+
KubernetesRoleBindingSchema(),
|
|
316
|
+
role_bindings,
|
|
317
|
+
lastupdated=update_tag,
|
|
318
|
+
CLUSTER_ID=cluster_id,
|
|
319
|
+
CLUSTER_NAME=cluster_name,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@timeit
|
|
324
|
+
def load_cluster_roles(
|
|
325
|
+
session: neo4j.Session,
|
|
326
|
+
cluster_roles: List[Dict[str, Any]],
|
|
327
|
+
update_tag: int,
|
|
328
|
+
cluster_id: str,
|
|
329
|
+
cluster_name: str,
|
|
330
|
+
) -> None:
|
|
331
|
+
logger.info(f"Loading {len(cluster_roles)} KubernetesClusterRoles")
|
|
332
|
+
load(
|
|
333
|
+
session,
|
|
334
|
+
KubernetesClusterRoleSchema(),
|
|
335
|
+
cluster_roles,
|
|
336
|
+
lastupdated=update_tag,
|
|
337
|
+
CLUSTER_ID=cluster_id,
|
|
338
|
+
CLUSTER_NAME=cluster_name,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@timeit
|
|
343
|
+
def load_cluster_role_bindings(
|
|
344
|
+
session: neo4j.Session,
|
|
345
|
+
cluster_role_bindings: List[Dict[str, Any]],
|
|
346
|
+
update_tag: int,
|
|
347
|
+
cluster_id: str,
|
|
348
|
+
cluster_name: str,
|
|
349
|
+
) -> None:
|
|
350
|
+
logger.info(f"Loading {len(cluster_role_bindings)} KubernetesClusterRoleBindings")
|
|
351
|
+
load(
|
|
352
|
+
session,
|
|
353
|
+
KubernetesClusterRoleBindingSchema(),
|
|
354
|
+
cluster_role_bindings,
|
|
355
|
+
lastupdated=update_tag,
|
|
356
|
+
CLUSTER_ID=cluster_id,
|
|
357
|
+
CLUSTER_NAME=cluster_name,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
@timeit
|
|
362
|
+
def cleanup(session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
|
|
363
|
+
logger.debug("Running cleanup job for Kubernetes RBAC resources")
|
|
364
|
+
cleanup_job = GraphJob.from_node_schema(
|
|
365
|
+
KubernetesServiceAccountSchema(), common_job_parameters
|
|
366
|
+
)
|
|
367
|
+
cleanup_job.run(session)
|
|
368
|
+
|
|
369
|
+
cleanup_job = GraphJob.from_node_schema(
|
|
370
|
+
KubernetesRoleSchema(), common_job_parameters
|
|
371
|
+
)
|
|
372
|
+
cleanup_job.run(session)
|
|
373
|
+
|
|
374
|
+
cleanup_job = GraphJob.from_node_schema(
|
|
375
|
+
KubernetesRoleBindingSchema(), common_job_parameters
|
|
376
|
+
)
|
|
377
|
+
cleanup_job.run(session)
|
|
378
|
+
|
|
379
|
+
cleanup_job = GraphJob.from_node_schema(
|
|
380
|
+
KubernetesClusterRoleSchema(), common_job_parameters
|
|
381
|
+
)
|
|
382
|
+
cleanup_job.run(session)
|
|
383
|
+
|
|
384
|
+
cleanup_job = GraphJob.from_node_schema(
|
|
385
|
+
KubernetesClusterRoleBindingSchema(), common_job_parameters
|
|
386
|
+
)
|
|
387
|
+
cleanup_job.run(session)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@timeit
|
|
391
|
+
def sync_kubernetes_rbac(
|
|
392
|
+
session: neo4j.Session,
|
|
393
|
+
client: K8sClient,
|
|
394
|
+
update_tag: int,
|
|
395
|
+
common_job_parameters: Dict[str, Any],
|
|
396
|
+
) -> None:
|
|
397
|
+
logger.info(f"Syncing Kubernetes RBAC resources for cluster {client.name}")
|
|
398
|
+
|
|
399
|
+
# Get namespace-scoped resources
|
|
400
|
+
service_accounts = get_service_accounts(client)
|
|
401
|
+
roles = get_roles(client)
|
|
402
|
+
role_bindings = get_role_bindings(client)
|
|
403
|
+
|
|
404
|
+
# Get cluster-scoped resources
|
|
405
|
+
cluster_roles = get_cluster_roles(client)
|
|
406
|
+
cluster_role_bindings = get_cluster_role_bindings(client)
|
|
407
|
+
|
|
408
|
+
# Transform namespace-scoped resources
|
|
409
|
+
transformed_service_accounts = transform_service_accounts(
|
|
410
|
+
service_accounts, client.name
|
|
411
|
+
)
|
|
412
|
+
transformed_roles = transform_roles(roles, client.name)
|
|
413
|
+
transformed_role_bindings = transform_role_bindings(role_bindings, client.name)
|
|
414
|
+
|
|
415
|
+
# Transform cluster-scoped resources
|
|
416
|
+
transformed_cluster_roles = transform_cluster_roles(cluster_roles, client.name)
|
|
417
|
+
transformed_cluster_role_bindings = transform_cluster_role_bindings(
|
|
418
|
+
cluster_role_bindings, client.name
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
cluster_id = common_job_parameters["CLUSTER_ID"]
|
|
422
|
+
cluster_name = client.name
|
|
423
|
+
|
|
424
|
+
load_service_accounts(
|
|
425
|
+
session=session,
|
|
426
|
+
service_accounts=transformed_service_accounts,
|
|
427
|
+
update_tag=update_tag,
|
|
428
|
+
cluster_id=cluster_id,
|
|
429
|
+
cluster_name=cluster_name,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
load_roles(
|
|
433
|
+
session=session,
|
|
434
|
+
roles=transformed_roles,
|
|
435
|
+
update_tag=update_tag,
|
|
436
|
+
cluster_id=cluster_id,
|
|
437
|
+
cluster_name=cluster_name,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
load_cluster_roles(
|
|
441
|
+
session=session,
|
|
442
|
+
cluster_roles=transformed_cluster_roles,
|
|
443
|
+
update_tag=update_tag,
|
|
444
|
+
cluster_id=cluster_id,
|
|
445
|
+
cluster_name=cluster_name,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
load_role_bindings(
|
|
449
|
+
session=session,
|
|
450
|
+
role_bindings=transformed_role_bindings,
|
|
451
|
+
update_tag=update_tag,
|
|
452
|
+
cluster_id=cluster_id,
|
|
453
|
+
cluster_name=cluster_name,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
load_cluster_role_bindings(
|
|
457
|
+
session=session,
|
|
458
|
+
cluster_role_bindings=transformed_cluster_role_bindings,
|
|
459
|
+
update_tag=update_tag,
|
|
460
|
+
cluster_id=cluster_id,
|
|
461
|
+
cluster_name=cluster_name,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
cleanup(session, common_job_parameters)
|
|
@@ -7,6 +7,7 @@ from kubernetes import config
|
|
|
7
7
|
from kubernetes.client import ApiClient
|
|
8
8
|
from kubernetes.client import CoreV1Api
|
|
9
9
|
from kubernetes.client import NetworkingV1Api
|
|
10
|
+
from kubernetes.client import RbacAuthorizationV1Api
|
|
10
11
|
from kubernetes.client import VersionApi
|
|
11
12
|
from kubernetes.client.exceptions import ApiException
|
|
12
13
|
|
|
@@ -62,6 +63,21 @@ class K8VersionApiClient(VersionApi):
|
|
|
62
63
|
super().__init__(api_client=api_client)
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
class K8RbacApiClient(RbacAuthorizationV1Api):
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
name: str,
|
|
70
|
+
config_file: str,
|
|
71
|
+
api_client: ApiClient | None = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
self.name = name
|
|
74
|
+
if not api_client:
|
|
75
|
+
api_client = config.new_client_from_config(
|
|
76
|
+
context=name, config_file=config_file
|
|
77
|
+
)
|
|
78
|
+
super().__init__(api_client=api_client)
|
|
79
|
+
|
|
80
|
+
|
|
65
81
|
class K8sClient:
|
|
66
82
|
def __init__(
|
|
67
83
|
self,
|
|
@@ -75,6 +91,7 @@ class K8sClient:
|
|
|
75
91
|
self.core = K8CoreApiClient(self.name, self.config_file)
|
|
76
92
|
self.networking = K8NetworkingApiClient(self.name, self.config_file)
|
|
77
93
|
self.version = K8VersionApiClient(self.name, self.config_file)
|
|
94
|
+
self.rbac = K8RbacApiClient(self.name, self.config_file)
|
|
78
95
|
|
|
79
96
|
|
|
80
97
|
def get_k8s_clients(kubeconfig: str) -> list[K8sClient]:
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
|
+
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
+
from cartography.models.core.relationships import OtherRelationships
|
|
11
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class CognitoIdentityPoolNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("IdentityPoolId")
|
|
17
|
+
arn: PropertyRef = PropertyRef("IdentityPoolId", extra_index=True)
|
|
18
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
19
|
+
roles: PropertyRef = PropertyRef("Roles")
|
|
20
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class CognitoIdentityPoolToAwsAccountRelProperties(CartographyRelProperties):
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class CognitoIdentityPoolToAWSAccountRel(CartographyRelSchema):
|
|
30
|
+
target_node_label: str = "AWSAccount"
|
|
31
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
32
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
33
|
+
)
|
|
34
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
35
|
+
rel_label: str = "RESOURCE"
|
|
36
|
+
properties: CognitoIdentityPoolToAwsAccountRelProperties = (
|
|
37
|
+
CognitoIdentityPoolToAwsAccountRelProperties()
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class CognitoIdentityPoolToAWSRoleRelProperties(CartographyRelProperties):
|
|
43
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class CognitoIdentityPoolToAWSRoleRel(CartographyRelSchema):
|
|
48
|
+
target_node_label: str = "AWSRole"
|
|
49
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
50
|
+
{"arn": PropertyRef("Roles", one_to_many=True)},
|
|
51
|
+
)
|
|
52
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
53
|
+
rel_label: str = "ASSOCIATED_WITH"
|
|
54
|
+
properties: CognitoIdentityPoolToAWSRoleRelProperties = (
|
|
55
|
+
CognitoIdentityPoolToAWSRoleRelProperties()
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class CognitoIdentityPoolSchema(CartographyNodeSchema):
|
|
61
|
+
label: str = "CognitoIdentityPool"
|
|
62
|
+
properties: CognitoIdentityPoolNodeProperties = CognitoIdentityPoolNodeProperties()
|
|
63
|
+
sub_resource_relationship: CognitoIdentityPoolToAWSAccountRel = (
|
|
64
|
+
CognitoIdentityPoolToAWSAccountRel()
|
|
65
|
+
)
|
|
66
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
67
|
+
[
|
|
68
|
+
CognitoIdentityPoolToAWSRoleRel(),
|
|
69
|
+
]
|
|
70
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
|
+
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class CognitoUserPoolNodeProperties(CartographyNodeProperties):
|
|
15
|
+
id: PropertyRef = PropertyRef("Id")
|
|
16
|
+
arn: PropertyRef = PropertyRef("Id", extra_index=True)
|
|
17
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
18
|
+
name: PropertyRef = PropertyRef("Name")
|
|
19
|
+
status: PropertyRef = PropertyRef("Status")
|
|
20
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class CognitoUserPoolToAwsAccountRelProperties(CartographyRelProperties):
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class CognitoUserPoolToAWSAccountRel(CartographyRelSchema):
|
|
30
|
+
target_node_label: str = "AWSAccount"
|
|
31
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
32
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
33
|
+
)
|
|
34
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
35
|
+
rel_label: str = "RESOURCE"
|
|
36
|
+
properties: CognitoUserPoolToAwsAccountRelProperties = (
|
|
37
|
+
CognitoUserPoolToAwsAccountRelProperties()
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class CognitoUserPoolSchema(CartographyNodeSchema):
|
|
43
|
+
label: str = "CognitoUserPool"
|
|
44
|
+
properties: CognitoUserPoolNodeProperties = CognitoUserPoolNodeProperties()
|
|
45
|
+
sub_resource_relationship: CognitoUserPoolToAWSAccountRel = (
|
|
46
|
+
CognitoUserPoolToAWSAccountRel()
|
|
47
|
+
)
|
|
@@ -50,7 +50,7 @@ class EC2SecurityGroupToVpcRel(CartographyRelSchema):
|
|
|
50
50
|
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
51
51
|
{"vpcid": PropertyRef("VpcId")}
|
|
52
52
|
)
|
|
53
|
-
direction: LinkDirection = LinkDirection.
|
|
53
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
54
54
|
rel_label: str = "MEMBER_OF_EC2_SECURITY_GROUP"
|
|
55
55
|
properties: EC2SecurityGroupToVpcRelProperties = (
|
|
56
56
|
EC2SecurityGroupToVpcRelProperties()
|
|
@@ -104,6 +104,22 @@ class ECSServiceToAWSAccountRel(CartographyRelSchema):
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class ECSServiceToECSTaskRelProperties(CartographyRelProperties):
|
|
109
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(frozen=True)
|
|
113
|
+
class ECSServiceToECSTaskRel(CartographyRelSchema):
|
|
114
|
+
target_node_label: str = "ECSTask"
|
|
115
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
116
|
+
{"service_name": PropertyRef("serviceName")}
|
|
117
|
+
)
|
|
118
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
119
|
+
rel_label: str = "HAS_TASK"
|
|
120
|
+
properties: ECSServiceToECSTaskRelProperties = ECSServiceToECSTaskRelProperties()
|
|
121
|
+
|
|
122
|
+
|
|
107
123
|
@dataclass(frozen=True)
|
|
108
124
|
class ECSServiceSchema(CartographyNodeSchema):
|
|
109
125
|
label: str = "ECSService"
|
|
@@ -113,5 +129,6 @@ class ECSServiceSchema(CartographyNodeSchema):
|
|
|
113
129
|
[
|
|
114
130
|
ECSServiceToECSClusterRel(),
|
|
115
131
|
ECSServiceToTaskDefinitionRel(),
|
|
132
|
+
ECSServiceToECSTaskRel(),
|
|
116
133
|
]
|
|
117
134
|
)
|
|
@@ -27,6 +27,7 @@ class ECSTaskNodeProperties(CartographyNodeProperties):
|
|
|
27
27
|
enable_execute_command: PropertyRef = PropertyRef("enableExecuteCommand")
|
|
28
28
|
execution_stopped_at: PropertyRef = PropertyRef("executionStoppedAt")
|
|
29
29
|
group: PropertyRef = PropertyRef("group")
|
|
30
|
+
service_name: PropertyRef = PropertyRef("serviceName")
|
|
30
31
|
health_status: PropertyRef = PropertyRef("healthStatus")
|
|
31
32
|
last_status: PropertyRef = PropertyRef("lastStatus")
|
|
32
33
|
launch_type: PropertyRef = PropertyRef("launchType")
|