cartography 0.112.0__py3-none-any.whl → 0.114.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/_version.py +2 -2
- cartography/cli.py +8 -0
- cartography/config.py +4 -0
- cartography/data/indexes.cypher +0 -31
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/iam.py +741 -492
- cartography/intel/aws/organizations.py +7 -8
- cartography/intel/aws/permission_relationships.py +4 -16
- cartography/intel/aws/resources.py +2 -0
- cartography/intel/azure/__init__.py +16 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/functions.py +124 -0
- cartography/intel/entra/__init__.py +31 -0
- cartography/intel/entra/app_role_assignments.py +277 -0
- cartography/intel/entra/applications.py +4 -238
- cartography/intel/entra/federation/__init__.py +0 -0
- cartography/intel/entra/federation/aws_identity_center.py +77 -0
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/gcp/__init__.py +136 -436
- cartography/intel/gcp/clients.py +65 -0
- cartography/intel/gcp/compute.py +18 -44
- cartography/intel/gcp/crm/__init__.py +0 -0
- cartography/intel/gcp/crm/folders.py +108 -0
- cartography/intel/gcp/crm/orgs.py +65 -0
- cartography/intel/gcp/crm/projects.py +109 -0
- cartography/intel/gcp/dns.py +82 -169
- cartography/intel/gcp/gke.py +72 -113
- cartography/intel/gcp/iam.py +66 -54
- cartography/intel/gcp/storage.py +75 -159
- cartography/intel/github/__init__.py +41 -0
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +73 -39
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -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 +26 -0
- cartography/models/aws/iam/inline_policy.py +78 -0
- cartography/models/aws/iam/managed_policy.py +51 -0
- cartography/models/aws/iam/policy_statement.py +57 -0
- cartography/models/aws/iam/role.py +83 -0
- cartography/models/aws/iam/root_principal.py +52 -0
- cartography/models/aws/iam/service_principal.py +30 -0
- cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
- cartography/models/aws/iam/user.py +54 -0
- cartography/models/azure/__init__.py +0 -0
- cartography/models/azure/app_service.py +59 -0
- cartography/models/azure/function_app.py +59 -0
- cartography/models/entra/entra_user_to_aws_sso.py +41 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/gcp/compute/subnet.py +74 -0
- cartography/models/gcp/crm/__init__.py +0 -0
- cartography/models/gcp/crm/folders.py +98 -0
- cartography/models/gcp/crm/organizations.py +21 -0
- cartography/models/gcp/crm/projects.py +100 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/gke.py +69 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- cartography/models/github/commits.py +63 -0
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/METADATA +7 -5
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/RECORD +69 -39
- cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
- cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
- 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/intel/gcp/crm.py +0 -355
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/WHEEL +0 -0
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/top_level.txt +0 -0
cartography/intel/gcp/dns.py
CHANGED
|
@@ -7,28 +7,20 @@ import neo4j
|
|
|
7
7
|
from googleapiclient.discovery import HttpError
|
|
8
8
|
from googleapiclient.discovery import Resource
|
|
9
9
|
|
|
10
|
-
from cartography.
|
|
10
|
+
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
12
|
+
from cartography.models.gcp.dns import GCPDNSZoneSchema
|
|
13
|
+
from cartography.models.gcp.dns import GCPRecordSetSchema
|
|
11
14
|
from cartography.util import timeit
|
|
12
15
|
|
|
13
16
|
logger = logging.getLogger(__name__)
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
@timeit
|
|
17
|
-
def get_dns_zones(dns: Resource, project_id: str) -> List[
|
|
18
|
-
"""
|
|
19
|
-
Returns a list of DNS zones within the given project.
|
|
20
|
-
|
|
21
|
-
:type dns: The GCP DNS resource object
|
|
22
|
-
:param dns: The DNS resource object created by googleapiclient.discovery.build()
|
|
23
|
-
|
|
24
|
-
:type project_id: str
|
|
25
|
-
:param project_id: Current Google Project Id
|
|
26
|
-
|
|
27
|
-
:rtype: list
|
|
28
|
-
:return: List of DNS zones
|
|
29
|
-
"""
|
|
20
|
+
def get_dns_zones(dns: Resource, project_id: str) -> List[Dict]:
|
|
21
|
+
"""Returns a list of DNS zones within the given project."""
|
|
30
22
|
try:
|
|
31
|
-
zones = []
|
|
23
|
+
zones: List[Dict] = []
|
|
32
24
|
request = dns.managedZones().list(project=project_id)
|
|
33
25
|
while request is not None:
|
|
34
26
|
response = request.execute()
|
|
@@ -47,40 +39,22 @@ def get_dns_zones(dns: Resource, project_id: str) -> List[Resource]:
|
|
|
47
39
|
):
|
|
48
40
|
logger.warning(
|
|
49
41
|
(
|
|
50
|
-
"Could not retrieve DNS zones on project %s due to permissions issues.
|
|
42
|
+
"Could not retrieve DNS zones on project %s due to permissions issues. "
|
|
43
|
+
"Code: %s, Message: %s"
|
|
51
44
|
),
|
|
52
45
|
project_id,
|
|
53
46
|
err["code"],
|
|
54
47
|
err["message"],
|
|
55
48
|
)
|
|
56
49
|
return []
|
|
57
|
-
|
|
58
|
-
raise
|
|
50
|
+
raise
|
|
59
51
|
|
|
60
52
|
|
|
61
53
|
@timeit
|
|
62
|
-
def get_dns_rrs(
|
|
63
|
-
|
|
64
|
-
dns_zones: List[Dict],
|
|
65
|
-
project_id: str,
|
|
66
|
-
) -> List[Resource]:
|
|
67
|
-
"""
|
|
68
|
-
Returns a list of DNS Resource Record Sets within the given project.
|
|
69
|
-
|
|
70
|
-
:type dns: The GCP DNS resource object
|
|
71
|
-
:param dns: The DNS resource object created by googleapiclient.discovery.build()
|
|
72
|
-
|
|
73
|
-
:type dns_zones: list
|
|
74
|
-
:param dns_zones: List of DNS zones for the project
|
|
75
|
-
|
|
76
|
-
:type project_id: str
|
|
77
|
-
:param project_id: Current Google Project Id
|
|
78
|
-
|
|
79
|
-
:rtype: list
|
|
80
|
-
:return: List of Resource Record Sets
|
|
81
|
-
"""
|
|
54
|
+
def get_dns_rrs(dns: Resource, dns_zones: List[Dict], project_id: str) -> List[Dict]:
|
|
55
|
+
"""Returns a list of DNS Resource Record Sets within the given project."""
|
|
82
56
|
try:
|
|
83
|
-
rrs: List[
|
|
57
|
+
rrs: List[Dict] = []
|
|
84
58
|
for zone in dns_zones:
|
|
85
59
|
request = dns.resourceRecordSets().list(
|
|
86
60
|
project=project_id,
|
|
@@ -104,16 +78,53 @@ def get_dns_rrs(
|
|
|
104
78
|
):
|
|
105
79
|
logger.warning(
|
|
106
80
|
(
|
|
107
|
-
"Could not retrieve DNS RRS on project %s due to permissions issues.
|
|
81
|
+
"Could not retrieve DNS RRS on project %s due to permissions issues. "
|
|
82
|
+
"Code: %s, Message: %s"
|
|
108
83
|
),
|
|
109
84
|
project_id,
|
|
110
85
|
err["code"],
|
|
111
86
|
err["message"],
|
|
112
87
|
)
|
|
113
88
|
return []
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
89
|
+
raise
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@timeit
|
|
93
|
+
def transform_dns_zones(dns_zones: List[Dict]) -> List[Dict]:
|
|
94
|
+
"""Transform raw DNS zone responses into Neo4j-ready dicts."""
|
|
95
|
+
zones: List[Dict] = []
|
|
96
|
+
for z in dns_zones:
|
|
97
|
+
zones.append(
|
|
98
|
+
{
|
|
99
|
+
"id": z["id"],
|
|
100
|
+
"name": z.get("name"),
|
|
101
|
+
"dns_name": z.get("dnsName"),
|
|
102
|
+
"description": z.get("description"),
|
|
103
|
+
"visibility": z.get("visibility"),
|
|
104
|
+
"kind": z.get("kind"),
|
|
105
|
+
"nameservers": z.get("nameServers"),
|
|
106
|
+
"created_at": z.get("creationTime"),
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
return zones
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@timeit
|
|
113
|
+
def transform_dns_rrs(dns_rrs: List[Dict]) -> List[Dict]:
|
|
114
|
+
"""Transform raw DNS record set responses into Neo4j-ready dicts."""
|
|
115
|
+
records: List[Dict] = []
|
|
116
|
+
for r in dns_rrs:
|
|
117
|
+
records.append(
|
|
118
|
+
{
|
|
119
|
+
"id": r["name"],
|
|
120
|
+
"name": r["name"],
|
|
121
|
+
"type": r.get("type"),
|
|
122
|
+
"ttl": r.get("ttl"),
|
|
123
|
+
"data": r.get("rrdatas"),
|
|
124
|
+
"zone_id": r.get("zone"),
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
return records
|
|
117
128
|
|
|
118
129
|
|
|
119
130
|
@timeit
|
|
@@ -123,102 +134,30 @@ def load_dns_zones(
|
|
|
123
134
|
project_id: str,
|
|
124
135
|
gcp_update_tag: int,
|
|
125
136
|
) -> None:
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
:param dns_resp: A DNS response object from the GKE API
|
|
134
|
-
|
|
135
|
-
:type project_id: str
|
|
136
|
-
:param project_id: Current Google Project Id
|
|
137
|
-
|
|
138
|
-
:type gcp_update_tag: timestamp
|
|
139
|
-
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
140
|
-
|
|
141
|
-
:rtype: NoneType
|
|
142
|
-
:return: Nothing
|
|
143
|
-
"""
|
|
144
|
-
|
|
145
|
-
ingest_records = """
|
|
146
|
-
UNWIND $records as record
|
|
147
|
-
MERGE(zone:GCPDNSZone{id:record.id})
|
|
148
|
-
ON CREATE SET
|
|
149
|
-
zone.firstseen = timestamp(),
|
|
150
|
-
zone.created_at = record.creationTime
|
|
151
|
-
SET
|
|
152
|
-
zone.name = record.name,
|
|
153
|
-
zone.dns_name = record.dnsName,
|
|
154
|
-
zone.description = record.description,
|
|
155
|
-
zone.visibility = record.visibility,
|
|
156
|
-
zone.kind = record.kind,
|
|
157
|
-
zone.nameservers = record.nameServers,
|
|
158
|
-
zone.lastupdated = $gcp_update_tag
|
|
159
|
-
WITH zone
|
|
160
|
-
MATCH (owner:GCPProject{id:$ProjectId})
|
|
161
|
-
MERGE (owner)-[r:RESOURCE]->(zone)
|
|
162
|
-
ON CREATE SET
|
|
163
|
-
r.firstseen = timestamp(),
|
|
164
|
-
r.lastupdated = $gcp_update_tag
|
|
165
|
-
"""
|
|
166
|
-
neo4j_session.run(
|
|
167
|
-
ingest_records,
|
|
168
|
-
records=dns_zones,
|
|
169
|
-
ProjectId=project_id,
|
|
170
|
-
gcp_update_tag=gcp_update_tag,
|
|
137
|
+
"""Ingest GCP DNS Zones into Neo4j."""
|
|
138
|
+
load(
|
|
139
|
+
neo4j_session,
|
|
140
|
+
GCPDNSZoneSchema(),
|
|
141
|
+
dns_zones,
|
|
142
|
+
lastupdated=gcp_update_tag,
|
|
143
|
+
PROJECT_ID=project_id,
|
|
171
144
|
)
|
|
172
145
|
|
|
173
146
|
|
|
174
147
|
@timeit
|
|
175
148
|
def load_rrs(
|
|
176
149
|
neo4j_session: neo4j.Session,
|
|
177
|
-
dns_rrs: List[
|
|
150
|
+
dns_rrs: List[Dict],
|
|
178
151
|
project_id: str,
|
|
179
152
|
gcp_update_tag: int,
|
|
180
153
|
) -> None:
|
|
181
|
-
"""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
:param dns_rrs: A list of RRS
|
|
189
|
-
|
|
190
|
-
:type project_id: str
|
|
191
|
-
:param project_id: Current Google Project Id
|
|
192
|
-
|
|
193
|
-
:type gcp_update_tag: timestamp
|
|
194
|
-
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
195
|
-
|
|
196
|
-
:rtype: NoneType
|
|
197
|
-
:return: Nothing
|
|
198
|
-
"""
|
|
199
|
-
|
|
200
|
-
ingest_records = """
|
|
201
|
-
UNWIND $records as record
|
|
202
|
-
MERGE(rrs:GCPRecordSet{id:record.name})
|
|
203
|
-
ON CREATE SET
|
|
204
|
-
rrs.firstseen = timestamp()
|
|
205
|
-
SET
|
|
206
|
-
rrs.name = record.name,
|
|
207
|
-
rrs.type = record.type,
|
|
208
|
-
rrs.ttl = record.ttl,
|
|
209
|
-
rrs.data = record.rrdatas,
|
|
210
|
-
rrs.lastupdated = $gcp_update_tag
|
|
211
|
-
WITH rrs, record
|
|
212
|
-
MATCH (zone:GCPDNSZone{id:record.zone})
|
|
213
|
-
MERGE (zone)-[r:HAS_RECORD]->(rrs)
|
|
214
|
-
ON CREATE SET
|
|
215
|
-
r.firstseen = timestamp(),
|
|
216
|
-
r.lastupdated = $gcp_update_tag
|
|
217
|
-
"""
|
|
218
|
-
neo4j_session.run(
|
|
219
|
-
ingest_records,
|
|
220
|
-
records=dns_rrs,
|
|
221
|
-
gcp_update_tag=gcp_update_tag,
|
|
154
|
+
"""Ingest GCP DNS Resource Record Sets into Neo4j."""
|
|
155
|
+
load(
|
|
156
|
+
neo4j_session,
|
|
157
|
+
GCPRecordSetSchema(),
|
|
158
|
+
dns_rrs,
|
|
159
|
+
lastupdated=gcp_update_tag,
|
|
160
|
+
PROJECT_ID=project_id,
|
|
222
161
|
)
|
|
223
162
|
|
|
224
163
|
|
|
@@ -227,19 +166,14 @@ def cleanup_dns_records(
|
|
|
227
166
|
neo4j_session: neo4j.Session,
|
|
228
167
|
common_job_parameters: Dict,
|
|
229
168
|
) -> None:
|
|
230
|
-
"""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
:rtype: NoneType
|
|
240
|
-
:return: Nothing
|
|
241
|
-
"""
|
|
242
|
-
run_cleanup_job("gcp_dns_cleanup.json", neo4j_session, common_job_parameters)
|
|
169
|
+
"""Delete out-of-date GCP DNS Zones and Record Sets nodes and relationships."""
|
|
170
|
+
# Record sets depend on zones, so clean them up first.
|
|
171
|
+
GraphJob.from_node_schema(GCPRecordSetSchema(), common_job_parameters).run(
|
|
172
|
+
neo4j_session,
|
|
173
|
+
)
|
|
174
|
+
GraphJob.from_node_schema(GCPDNSZoneSchema(), common_job_parameters).run(
|
|
175
|
+
neo4j_session,
|
|
176
|
+
)
|
|
243
177
|
|
|
244
178
|
|
|
245
179
|
@timeit
|
|
@@ -250,33 +184,12 @@ def sync(
|
|
|
250
184
|
gcp_update_tag: int,
|
|
251
185
|
common_job_parameters: Dict,
|
|
252
186
|
) -> None:
|
|
253
|
-
"""
|
|
254
|
-
Get GCP DNS Zones and Resource Record Sets using the DNS resource object, ingest to Neo4j, and clean up old data.
|
|
255
|
-
|
|
256
|
-
:type neo4j_session: The Neo4j session object
|
|
257
|
-
:param neo4j_session: The Neo4j session
|
|
258
|
-
|
|
259
|
-
:type dns: The DNS resource object created by googleapiclient.discovery.build()
|
|
260
|
-
:param dns: The GCP DNS resource object
|
|
261
|
-
|
|
262
|
-
:type project_id: str
|
|
263
|
-
:param project_id: The project ID of the corresponding project
|
|
264
|
-
|
|
265
|
-
:type gcp_update_tag: timestamp
|
|
266
|
-
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
267
|
-
|
|
268
|
-
:type common_job_parameters: dict
|
|
269
|
-
:param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
|
|
270
|
-
|
|
271
|
-
:rtype: NoneType
|
|
272
|
-
:return: Nothing
|
|
273
|
-
"""
|
|
187
|
+
"""Get GCP DNS Zones and Record Sets, load them into Neo4j, and clean up old data."""
|
|
274
188
|
logger.info("Syncing DNS records for project %s.", project_id)
|
|
275
|
-
|
|
276
|
-
dns_zones =
|
|
189
|
+
dns_zones_resp = get_dns_zones(dns, project_id)
|
|
190
|
+
dns_zones = transform_dns_zones(dns_zones_resp)
|
|
277
191
|
load_dns_zones(neo4j_session, dns_zones, project_id, gcp_update_tag)
|
|
278
|
-
|
|
279
|
-
dns_rrs =
|
|
192
|
+
dns_rrs_resp = get_dns_rrs(dns, dns_zones_resp, project_id)
|
|
193
|
+
dns_rrs = transform_dns_rrs(dns_rrs_resp)
|
|
280
194
|
load_rrs(neo4j_session, dns_rrs, project_id, gcp_update_tag)
|
|
281
|
-
# TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
|
|
282
195
|
cleanup_dns_records(neo4j_session, common_job_parameters)
|
cartography/intel/gcp/gke.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
from typing import Any
|
|
3
4
|
from typing import Dict
|
|
5
|
+
from typing import List
|
|
4
6
|
|
|
5
7
|
import neo4j
|
|
6
8
|
from googleapiclient.discovery import HttpError
|
|
7
9
|
from googleapiclient.discovery import Resource
|
|
8
10
|
|
|
9
|
-
from cartography.
|
|
11
|
+
from cartography.client.core.tx import load
|
|
12
|
+
from cartography.graph.job import GraphJob
|
|
13
|
+
from cartography.models.gcp.gke import GCPGKEClusterSchema
|
|
10
14
|
from cartography.util import timeit
|
|
11
15
|
|
|
12
16
|
logger = logging.getLogger(__name__)
|
|
@@ -56,105 +60,20 @@ def load_gke_clusters(
|
|
|
56
60
|
gcp_update_tag: int,
|
|
57
61
|
) -> None:
|
|
58
62
|
"""
|
|
59
|
-
Ingest GCP GKE
|
|
60
|
-
|
|
61
|
-
:type neo4j_session: Neo4j session object
|
|
62
|
-
:param neo4j session: The Neo4j session object
|
|
63
|
-
|
|
64
|
-
:type cluster_resp: Dict
|
|
65
|
-
:param cluster_resp: A cluster response object from the GKE API
|
|
66
|
-
|
|
67
|
-
:type gcp_update_tag: timestamp
|
|
68
|
-
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
69
|
-
|
|
70
|
-
:rtype: NoneType
|
|
71
|
-
:return: Nothing
|
|
63
|
+
Ingest GCP GKE clusters using the data model loader.
|
|
72
64
|
"""
|
|
65
|
+
clusters: List[Dict[str, Any]] = transform_gke_clusters(cluster_resp)
|
|
73
66
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
cluster.monitoring_service = $ClusterMonitoringService,
|
|
85
|
-
cluster.network = $ClusterNetwork,
|
|
86
|
-
cluster.subnetwork = $ClusterSubnetwork,
|
|
87
|
-
cluster.cluster_ipv4cidr = $ClusterIPv4Cidr,
|
|
88
|
-
cluster.zone = $ClusterZone,
|
|
89
|
-
cluster.location = $ClusterLocation,
|
|
90
|
-
cluster.endpoint = $ClusterEndpoint,
|
|
91
|
-
cluster.initial_version = $ClusterInitialVersion,
|
|
92
|
-
cluster.current_master_version = $ClusterMasterVersion,
|
|
93
|
-
cluster.status = $ClusterStatus,
|
|
94
|
-
cluster.services_ipv4cidr = $ClusterServicesIPv4Cidr,
|
|
95
|
-
cluster.database_encryption = $ClusterDatabaseEncryption,
|
|
96
|
-
cluster.network_policy = $ClusterNetworkPolicy,
|
|
97
|
-
cluster.master_authorized_networks = $ClusterMasterAuthorizedNetworks,
|
|
98
|
-
cluster.legacy_abac = $ClusterAbac,
|
|
99
|
-
cluster.shielded_nodes = $ClusterShieldedNodes,
|
|
100
|
-
cluster.private_nodes = $ClusterPrivateNodes,
|
|
101
|
-
cluster.private_endpoint_enabled = $ClusterPrivateEndpointEnabled,
|
|
102
|
-
cluster.private_endpoint = $ClusterPrivateEndpoint,
|
|
103
|
-
cluster.public_endpoint = $ClusterPublicEndpoint,
|
|
104
|
-
cluster.masterauth_username = $ClusterMasterUsername,
|
|
105
|
-
cluster.masterauth_password = $ClusterMasterPassword
|
|
106
|
-
WITH cluster
|
|
107
|
-
MATCH (owner:GCPProject{id:$ProjectId})
|
|
108
|
-
MERGE (owner)-[r:RESOURCE]->(cluster)
|
|
109
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
110
|
-
SET r.lastupdated = $gcp_update_tag
|
|
111
|
-
"""
|
|
112
|
-
for cluster in cluster_resp.get("clusters", []):
|
|
113
|
-
neo4j_session.run(
|
|
114
|
-
query,
|
|
115
|
-
ProjectId=project_id,
|
|
116
|
-
ClusterSelfLink=cluster["selfLink"],
|
|
117
|
-
ClusterCreateTime=cluster["createTime"],
|
|
118
|
-
ClusterName=cluster["name"],
|
|
119
|
-
ClusterDescription=cluster.get("description"),
|
|
120
|
-
ClusterLoggingService=cluster.get("loggingService"),
|
|
121
|
-
ClusterMonitoringService=cluster.get("monitoringService"),
|
|
122
|
-
ClusterNetwork=cluster.get("network"),
|
|
123
|
-
ClusterSubnetwork=cluster.get("subnetwork"),
|
|
124
|
-
ClusterIPv4Cidr=cluster.get("clusterIpv4Cidr"),
|
|
125
|
-
ClusterZone=cluster.get("zone"),
|
|
126
|
-
ClusterLocation=cluster.get("location"),
|
|
127
|
-
ClusterEndpoint=cluster.get("endpoint"),
|
|
128
|
-
ClusterInitialVersion=cluster.get("initialClusterVersion"),
|
|
129
|
-
ClusterMasterVersion=cluster.get("currentMasterVersion"),
|
|
130
|
-
ClusterStatus=cluster.get("status"),
|
|
131
|
-
ClusterServicesIPv4Cidr=cluster.get("servicesIpv4Cidr"),
|
|
132
|
-
ClusterDatabaseEncryption=cluster.get("databaseEncryption", {}).get(
|
|
133
|
-
"state",
|
|
134
|
-
),
|
|
135
|
-
ClusterNetworkPolicy=_process_network_policy(cluster),
|
|
136
|
-
ClusterMasterAuthorizedNetworks=cluster.get(
|
|
137
|
-
"masterAuthorizedNetworksConfig",
|
|
138
|
-
{},
|
|
139
|
-
).get("enabled"),
|
|
140
|
-
ClusterAbac=cluster.get("legacyAbac", {}).get("enabled"),
|
|
141
|
-
ClusterShieldedNodes=cluster.get("shieldedNodes", {}).get("enabled"),
|
|
142
|
-
ClusterPrivateNodes=cluster.get("privateClusterConfig", {}).get(
|
|
143
|
-
"enablePrivateNodes",
|
|
144
|
-
),
|
|
145
|
-
ClusterPrivateEndpointEnabled=cluster.get("privateClusterConfig", {}).get(
|
|
146
|
-
"enablePrivateEndpoint",
|
|
147
|
-
),
|
|
148
|
-
ClusterPrivateEndpoint=cluster.get("privateClusterConfig", {}).get(
|
|
149
|
-
"privateEndpoint",
|
|
150
|
-
),
|
|
151
|
-
ClusterPublicEndpoint=cluster.get("privateClusterConfig", {}).get(
|
|
152
|
-
"publicEndpoint",
|
|
153
|
-
),
|
|
154
|
-
ClusterMasterUsername=cluster.get("masterAuth", {}).get("username"),
|
|
155
|
-
ClusterMasterPassword=cluster.get("masterAuth", {}).get("password"),
|
|
156
|
-
gcp_update_tag=gcp_update_tag,
|
|
157
|
-
)
|
|
67
|
+
if not clusters:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
load(
|
|
71
|
+
neo4j_session,
|
|
72
|
+
GCPGKEClusterSchema(),
|
|
73
|
+
clusters,
|
|
74
|
+
lastupdated=gcp_update_tag,
|
|
75
|
+
PROJECT_ID=project_id,
|
|
76
|
+
)
|
|
158
77
|
|
|
159
78
|
|
|
160
79
|
def _process_network_policy(cluster: Dict) -> bool:
|
|
@@ -175,21 +94,10 @@ def cleanup_gke_clusters(
|
|
|
175
94
|
common_job_parameters: Dict,
|
|
176
95
|
) -> None:
|
|
177
96
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
:type neo4j_session: The Neo4j session object
|
|
181
|
-
:param neo4j_session: The Neo4j session
|
|
182
|
-
|
|
183
|
-
:type common_job_parameters: dict
|
|
184
|
-
:param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
|
|
185
|
-
|
|
186
|
-
:rtype: NoneType
|
|
187
|
-
:return: Nothing
|
|
97
|
+
Scoped cleanup for GKE clusters based on the project sub-resource relationship.
|
|
188
98
|
"""
|
|
189
|
-
|
|
190
|
-
"gcp_gke_cluster_cleanup.json",
|
|
99
|
+
GraphJob.from_node_schema(GCPGKEClusterSchema(), common_job_parameters).run(
|
|
191
100
|
neo4j_session,
|
|
192
|
-
common_job_parameters,
|
|
193
101
|
)
|
|
194
102
|
|
|
195
103
|
|
|
@@ -222,8 +130,59 @@ def sync_gke_clusters(
|
|
|
222
130
|
:rtype: NoneType
|
|
223
131
|
:return: Nothing
|
|
224
132
|
"""
|
|
225
|
-
logger.info("Syncing
|
|
133
|
+
logger.info("Syncing GKE clusters for project %s.", project_id)
|
|
226
134
|
gke_res = get_gke_clusters(container, project_id)
|
|
227
135
|
load_gke_clusters(neo4j_session, gke_res, project_id, gcp_update_tag)
|
|
228
|
-
# TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
|
|
229
136
|
cleanup_gke_clusters(neo4j_session, common_job_parameters)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def transform_gke_clusters(api_result: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
140
|
+
"""
|
|
141
|
+
Transform GKE API response into a list of dicts suitable for the data model loader.
|
|
142
|
+
"""
|
|
143
|
+
result: List[Dict[str, Any]] = []
|
|
144
|
+
for c in api_result.get("clusters", []):
|
|
145
|
+
transformed: Dict[str, Any] = {
|
|
146
|
+
# Required fields
|
|
147
|
+
"id": c["selfLink"],
|
|
148
|
+
"self_link": c["selfLink"],
|
|
149
|
+
"name": c["name"],
|
|
150
|
+
"created_at": c.get("createTime"),
|
|
151
|
+
# Optional fields
|
|
152
|
+
"description": c.get("description"),
|
|
153
|
+
"logging_service": c.get("loggingService"),
|
|
154
|
+
"monitoring_service": c.get("monitoringService"),
|
|
155
|
+
"network": c.get("network"),
|
|
156
|
+
"subnetwork": c.get("subnetwork"),
|
|
157
|
+
"cluster_ipv4cidr": c.get("clusterIpv4Cidr"),
|
|
158
|
+
"zone": c.get("zone"),
|
|
159
|
+
"location": c.get("location"),
|
|
160
|
+
"endpoint": c.get("endpoint"),
|
|
161
|
+
"initial_version": c.get("initialClusterVersion"),
|
|
162
|
+
"current_master_version": c.get("currentMasterVersion"),
|
|
163
|
+
"status": c.get("status"),
|
|
164
|
+
"services_ipv4cidr": c.get("servicesIpv4Cidr"),
|
|
165
|
+
"database_encryption": (c.get("databaseEncryption", {}) or {}).get("state"),
|
|
166
|
+
"network_policy": _process_network_policy(c),
|
|
167
|
+
"master_authorized_networks": (
|
|
168
|
+
c.get("masterAuthorizedNetworksConfig", {}) or {}
|
|
169
|
+
).get("enabled"),
|
|
170
|
+
"legacy_abac": (c.get("legacyAbac", {}) or {}).get("enabled"),
|
|
171
|
+
"shielded_nodes": (c.get("shieldedNodes", {}) or {}).get("enabled"),
|
|
172
|
+
"private_nodes": (c.get("privateClusterConfig", {}) or {}).get(
|
|
173
|
+
"enablePrivateNodes"
|
|
174
|
+
),
|
|
175
|
+
"private_endpoint_enabled": (c.get("privateClusterConfig", {}) or {}).get(
|
|
176
|
+
"enablePrivateEndpoint"
|
|
177
|
+
),
|
|
178
|
+
"private_endpoint": (c.get("privateClusterConfig", {}) or {}).get(
|
|
179
|
+
"privateEndpoint"
|
|
180
|
+
),
|
|
181
|
+
"public_endpoint": (c.get("privateClusterConfig", {}) or {}).get(
|
|
182
|
+
"publicEndpoint"
|
|
183
|
+
),
|
|
184
|
+
"masterauth_username": (c.get("masterAuth", {}) or {}).get("username"),
|
|
185
|
+
"masterauth_password": (c.get("masterAuth", {}) or {}).get("password"),
|
|
186
|
+
}
|
|
187
|
+
result.append(transformed)
|
|
188
|
+
return result
|