cartography 0.111.0rc1__py3-none-any.whl → 0.112.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 +57 -0
- cartography/config.py +24 -0
- cartography/data/indexes.cypher +0 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/intel/aws/apigateway.py +128 -17
- cartography/intel/aws/ec2/instances.py +3 -1
- cartography/intel/aws/ec2/network_interfaces.py +1 -1
- cartography/intel/aws/ec2/vpc_peerings.py +262 -125
- cartography/intel/azure/__init__.py +35 -32
- cartography/intel/azure/subscription.py +2 -2
- cartography/intel/azure/tenant.py +39 -30
- cartography/intel/azure/util/credentials.py +49 -174
- cartography/intel/entra/__init__.py +47 -1
- cartography/intel/entra/applications.py +220 -170
- cartography/intel/entra/groups.py +41 -22
- cartography/intel/entra/ou.py +28 -20
- cartography/intel/entra/users.py +24 -18
- cartography/intel/gcp/__init__.py +25 -8
- cartography/intel/gcp/compute.py +47 -12
- cartography/intel/github/repos.py +19 -10
- cartography/intel/github/util.py +12 -0
- cartography/intel/keycloak/__init__.py +153 -0
- cartography/intel/keycloak/authenticationexecutions.py +322 -0
- cartography/intel/keycloak/authenticationflows.py +77 -0
- cartography/intel/keycloak/clients.py +187 -0
- cartography/intel/keycloak/groups.py +126 -0
- cartography/intel/keycloak/identityproviders.py +94 -0
- cartography/intel/keycloak/organizations.py +163 -0
- cartography/intel/keycloak/realms.py +61 -0
- cartography/intel/keycloak/roles.py +202 -0
- cartography/intel/keycloak/scopes.py +73 -0
- cartography/intel/keycloak/users.py +70 -0
- cartography/intel/keycloak/util.py +47 -0
- cartography/intel/kubernetes/__init__.py +26 -0
- cartography/intel/kubernetes/eks.py +402 -0
- cartography/intel/kubernetes/rbac.py +133 -0
- cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
- cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
- cartography/models/aws/ec2/vpc_peering.py +157 -0
- cartography/models/azure/principal.py +44 -0
- cartography/models/azure/tenant.py +20 -0
- cartography/models/keycloak/__init__.py +0 -0
- cartography/models/keycloak/authenticationexecution.py +160 -0
- cartography/models/keycloak/authenticationflow.py +54 -0
- cartography/models/keycloak/client.py +177 -0
- cartography/models/keycloak/group.py +101 -0
- cartography/models/keycloak/identityprovider.py +89 -0
- cartography/models/keycloak/organization.py +116 -0
- cartography/models/keycloak/organizationdomain.py +73 -0
- cartography/models/keycloak/realm.py +173 -0
- cartography/models/keycloak/role.py +126 -0
- cartography/models/keycloak/scope.py +73 -0
- cartography/models/keycloak/user.py +51 -0
- cartography/models/kubernetes/clusterrolebindings.py +40 -0
- cartography/models/kubernetes/groups.py +107 -0
- cartography/models/kubernetes/oidc.py +51 -0
- cartography/models/kubernetes/rolebindings.py +40 -0
- cartography/models/kubernetes/users.py +105 -0
- cartography/sync.py +2 -0
- cartography/util.py +10 -0
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/METADATA +9 -5
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/RECORD +67 -34
- cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/WHEEL +0 -0
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.111.0rc1.dist-info → cartography-0.112.0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Any
|
|
2
3
|
from typing import Dict
|
|
3
4
|
from typing import List
|
|
5
|
+
from typing import Tuple
|
|
4
6
|
|
|
5
7
|
import boto3
|
|
6
8
|
import neo4j
|
|
7
9
|
|
|
10
|
+
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
12
|
+
from cartography.models.aws.ec2.vpc import AWSVpcSchema
|
|
13
|
+
from cartography.models.aws.ec2.vpc_cidr import AWSIPv4CidrBlockSchema
|
|
14
|
+
from cartography.models.aws.ec2.vpc_peering import AWSAccountVPCPeeringSchema
|
|
15
|
+
from cartography.models.aws.ec2.vpc_peering import AWSPeeringConnectionSchema
|
|
8
16
|
from cartography.util import aws_handle_regions
|
|
9
|
-
from cartography.util import run_cleanup_job
|
|
10
17
|
from cartography.util import timeit
|
|
11
18
|
|
|
12
19
|
from .util import get_botocore_config
|
|
@@ -29,148 +36,258 @@ def get_vpc_peerings_data(
|
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
@timeit
|
|
32
|
-
def
|
|
39
|
+
def transform_vpc_peering_data(
|
|
40
|
+
vpc_peerings: List[Dict],
|
|
41
|
+
) -> Tuple[
|
|
42
|
+
List[Dict[str, Any]],
|
|
43
|
+
List[Dict[str, Any]],
|
|
44
|
+
List[Dict[str, Any]],
|
|
45
|
+
List[Dict[str, Any]],
|
|
46
|
+
]:
|
|
47
|
+
transformed_peerings: List[Dict[str, Any]] = []
|
|
48
|
+
accepter_cidr_blocks: List[Dict[str, Any]] = []
|
|
49
|
+
requester_cidr_blocks: List[Dict[str, Any]] = []
|
|
50
|
+
vpc_nodes: List[Dict[str, Any]] = []
|
|
51
|
+
|
|
52
|
+
for peering in vpc_peerings:
|
|
53
|
+
accepter_cidr_ids: List[str] = []
|
|
54
|
+
for c_b in peering.get("AccepterVpcInfo", {}).get("CidrBlockSet", []):
|
|
55
|
+
block_id = f"{peering.get('AccepterVpcInfo', {}).get('VpcId')}|{c_b.get('CidrBlock')}"
|
|
56
|
+
accepter_cidr_blocks.append(
|
|
57
|
+
{
|
|
58
|
+
"Id": block_id,
|
|
59
|
+
"VpcId": peering.get("AccepterVpcInfo", {}).get("VpcId"),
|
|
60
|
+
"AssociationId": c_b.get("AssociationId"),
|
|
61
|
+
"CidrBlock": c_b.get("CidrBlock"),
|
|
62
|
+
"BlockState": c_b.get("CidrBlockState", {}).get("State"),
|
|
63
|
+
"BlockStateMessage": c_b.get("CidrBlockState", {}).get(
|
|
64
|
+
"StatusMessage",
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
accepter_cidr_ids.append(block_id)
|
|
69
|
+
|
|
70
|
+
requester_cidr_ids: List[str] = []
|
|
71
|
+
for c_b in peering.get("RequesterVpcInfo", {}).get("CidrBlockSet", []):
|
|
72
|
+
block_id = f"{peering.get('RequesterVpcInfo', {}).get('VpcId')}|{c_b.get('CidrBlock')}"
|
|
73
|
+
requester_cidr_blocks.append(
|
|
74
|
+
{
|
|
75
|
+
"Id": block_id,
|
|
76
|
+
"VpcId": peering.get("RequesterVpcInfo", {}).get("VpcId"),
|
|
77
|
+
"AssociationId": c_b.get("AssociationId"),
|
|
78
|
+
"CidrBlock": c_b.get("CidrBlock"),
|
|
79
|
+
"BlockState": c_b.get("CidrBlockState", {}).get("State"),
|
|
80
|
+
"BlockStateMessage": c_b.get("CidrBlockState", {}).get(
|
|
81
|
+
"StatusMessage",
|
|
82
|
+
),
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
requester_cidr_ids.append(block_id)
|
|
86
|
+
|
|
87
|
+
# Create VPC nodes for accepter and requester VPCs
|
|
88
|
+
accepter_vpc_id = peering.get("AccepterVpcInfo", {}).get("VpcId")
|
|
89
|
+
accepter_owner_id = peering.get("AccepterVpcInfo", {}).get("OwnerId")
|
|
90
|
+
if accepter_vpc_id:
|
|
91
|
+
vpc_nodes.append(
|
|
92
|
+
{
|
|
93
|
+
"VpcId": accepter_vpc_id,
|
|
94
|
+
"PrimaryCIDRBlock": None, # VPCs from peering data don't have complete info
|
|
95
|
+
"InstanceTenancy": None,
|
|
96
|
+
"State": None,
|
|
97
|
+
"IsDefault": None,
|
|
98
|
+
"DhcpOptionsId": None,
|
|
99
|
+
"AccountId": accepter_owner_id, # Account that owns this VPC
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
requester_vpc_id = peering.get("RequesterVpcInfo", {}).get("VpcId")
|
|
104
|
+
requester_owner_id = peering.get("RequesterVpcInfo", {}).get("OwnerId")
|
|
105
|
+
if requester_vpc_id:
|
|
106
|
+
vpc_nodes.append(
|
|
107
|
+
{
|
|
108
|
+
"VpcId": requester_vpc_id,
|
|
109
|
+
"PrimaryCIDRBlock": None, # VPCs from peering data don't have complete info
|
|
110
|
+
"InstanceTenancy": None,
|
|
111
|
+
"State": None,
|
|
112
|
+
"IsDefault": None,
|
|
113
|
+
"DhcpOptionsId": None,
|
|
114
|
+
"AccountId": requester_owner_id, # Account that owns this VPC
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
transformed_peerings.append(
|
|
119
|
+
{
|
|
120
|
+
"VpcPeeringConnectionId": peering.get("VpcPeeringConnectionId"),
|
|
121
|
+
"AllowDnsResolutionFromRemoteVpc": peering.get(
|
|
122
|
+
"RequesterVpcInfo",
|
|
123
|
+
{},
|
|
124
|
+
)
|
|
125
|
+
.get("PeeringOptions", {})
|
|
126
|
+
.get(
|
|
127
|
+
"AllowDnsResolutionFromRemoteVpc",
|
|
128
|
+
),
|
|
129
|
+
"AllowEgressFromLocalClassicLinkToRemoteVpc": peering.get(
|
|
130
|
+
"RequesterVpcInfo",
|
|
131
|
+
{},
|
|
132
|
+
)
|
|
133
|
+
.get("PeeringOptions", {})
|
|
134
|
+
.get(
|
|
135
|
+
"AllowEgressFromLocalClassicLinkToRemoteVpc",
|
|
136
|
+
),
|
|
137
|
+
"AllowEgressFromLocalVpcToRemoteClassicLink": peering.get(
|
|
138
|
+
"RequesterVpcInfo",
|
|
139
|
+
{},
|
|
140
|
+
)
|
|
141
|
+
.get("PeeringOptions", {})
|
|
142
|
+
.get(
|
|
143
|
+
"AllowEgressFromLocalVpcToRemoteClassicLink",
|
|
144
|
+
),
|
|
145
|
+
"RequesterRegion": peering.get("RequesterVpcInfo", {}).get(
|
|
146
|
+
"Region",
|
|
147
|
+
),
|
|
148
|
+
"AccepterRegion": peering.get("AccepterVpcInfo", {}).get(
|
|
149
|
+
"Region",
|
|
150
|
+
),
|
|
151
|
+
"StatusCode": peering.get("Status", {}).get("Code"),
|
|
152
|
+
"StatusMessage": peering.get("Status", {}).get("Message"),
|
|
153
|
+
"AccepterVpcId": peering.get("AccepterVpcInfo", {}).get("VpcId"),
|
|
154
|
+
"RequesterVpcId": peering.get("RequesterVpcInfo", {}).get(
|
|
155
|
+
"VpcId",
|
|
156
|
+
),
|
|
157
|
+
"ACCEPTER_CIDR_BLOCK_IDS": accepter_cidr_ids,
|
|
158
|
+
"REQUESTER_CIDR_BLOCK_IDS": requester_cidr_ids,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return transformed_peerings, accepter_cidr_blocks, requester_cidr_blocks, vpc_nodes
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@timeit
|
|
166
|
+
def transform_aws_accounts_from_vpc_peering(
|
|
167
|
+
vpc_peerings: List[Dict],
|
|
168
|
+
) -> List[Dict[str, Any]]:
|
|
169
|
+
"""
|
|
170
|
+
Transform VPC peering data to extract AWS account information.
|
|
171
|
+
Creates composite AWS account nodes with context from VPC peering.
|
|
172
|
+
"""
|
|
173
|
+
account_data: Dict[str, Dict[str, Any]] = {}
|
|
174
|
+
|
|
175
|
+
for peering in vpc_peerings:
|
|
176
|
+
# Extract accepter account
|
|
177
|
+
accepter_owner_id = peering.get("AccepterVpcInfo", {}).get("OwnerId")
|
|
178
|
+
if accepter_owner_id:
|
|
179
|
+
account_data[accepter_owner_id] = {
|
|
180
|
+
"id": accepter_owner_id,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Extract requester account
|
|
184
|
+
requester_owner_id = peering.get("RequesterVpcInfo", {}).get("OwnerId")
|
|
185
|
+
if requester_owner_id:
|
|
186
|
+
account_data[requester_owner_id] = {
|
|
187
|
+
"id": requester_owner_id,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return list(account_data.values())
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@timeit
|
|
194
|
+
def load_accepter_cidrs(
|
|
33
195
|
neo4j_session: neo4j.Session,
|
|
34
|
-
|
|
196
|
+
accepter_cidrs: List[Dict[str, Any]],
|
|
35
197
|
region: str,
|
|
36
198
|
aws_account_id: str,
|
|
37
199
|
update_tag: int,
|
|
38
200
|
) -> None:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
SET pcx.lastupdated = $update_tag,
|
|
45
|
-
pcx.allow_dns_resolution_from_remote_vpc =
|
|
46
|
-
vpc_peering.RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc,
|
|
47
|
-
pcx.allow_egress_from_local_classic_link_to_remote_vpc =
|
|
48
|
-
vpc_peering.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc,
|
|
49
|
-
pcx.allow_egress_from_local_vpc_to_remote_classic_link =
|
|
50
|
-
vpc_peering.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink,
|
|
51
|
-
pcx.requester_region = vpc_peering.RequesterVpcInfo.Region,
|
|
52
|
-
pcx.accepter_region = vpc_peering.AccepterVpcInfo.Region,
|
|
53
|
-
pcx.status_code = vpc_peering.Status.Code,
|
|
54
|
-
pcx.status_message = vpc_peering.Status.Message
|
|
55
|
-
|
|
56
|
-
MERGE (avpc:AWSVpc{id: vpc_peering.AccepterVpcInfo.VpcId})
|
|
57
|
-
ON CREATE SET avpc.firstseen = timestamp()
|
|
58
|
-
SET avpc.lastupdated = $update_tag, avpc.vpcid = vpc_peering.AccepterVpcInfo.VpcId
|
|
59
|
-
|
|
60
|
-
MERGE (rvpc:AWSVpc{id: vpc_peering.RequesterVpcInfo.VpcId})
|
|
61
|
-
ON CREATE SET rvpc.firstseen = timestamp()
|
|
62
|
-
SET rvpc.lastupdated = $update_tag, rvpc.vpcid = vpc_peering.RequesterVpcInfo.VpcId
|
|
63
|
-
|
|
64
|
-
MERGE (aaccount:AWSAccount{id: vpc_peering.AccepterVpcInfo.OwnerId})
|
|
65
|
-
ON CREATE SET aaccount.firstseen = timestamp()
|
|
66
|
-
SET aaccount.lastupdated = $update_tag
|
|
67
|
-
|
|
68
|
-
MERGE (raccount:AWSAccount{id: vpc_peering.RequesterVpcInfo.OwnerId})
|
|
69
|
-
ON CREATE SET raccount.firstseen = timestamp()
|
|
70
|
-
SET raccount.lastupdated = $update_tag
|
|
71
|
-
|
|
72
|
-
MERGE (pcx)-[rav:ACCEPTER_VPC]->(avpc)
|
|
73
|
-
ON CREATE SET rav.firstseen = timestamp()
|
|
74
|
-
SET rav.lastupdated = $update_tag
|
|
75
|
-
|
|
76
|
-
MERGE (aaccount)-[ra:RESOURCE]->(avpc)
|
|
77
|
-
ON CREATE SET ra.firstseen = timestamp()
|
|
78
|
-
SET ra.lastupdated = $update_tag
|
|
79
|
-
|
|
80
|
-
MERGE (pcx)-[rrv:REQUESTER_VPC]->(rvpc)
|
|
81
|
-
ON CREATE SET rrv.firstseen = timestamp()
|
|
82
|
-
SET rrv.lastupdated = $update_tag
|
|
83
|
-
|
|
84
|
-
MERGE (raccount)-[rr:RESOURCE]->(rvpc)
|
|
85
|
-
ON CREATE SET rr.firstseen = timestamp()
|
|
86
|
-
SET rr.lastupdated = $update_tag
|
|
87
|
-
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
neo4j_session.run(
|
|
91
|
-
ingest_vpc_peerings,
|
|
92
|
-
vpc_peerings=data,
|
|
93
|
-
update_tag=update_tag,
|
|
94
|
-
region=region,
|
|
95
|
-
aws_account_id=aws_account_id,
|
|
201
|
+
load(
|
|
202
|
+
neo4j_session,
|
|
203
|
+
AWSIPv4CidrBlockSchema(),
|
|
204
|
+
accepter_cidrs,
|
|
205
|
+
lastupdated=update_tag,
|
|
96
206
|
)
|
|
97
207
|
|
|
98
208
|
|
|
99
209
|
@timeit
|
|
100
|
-
def
|
|
210
|
+
def load_requester_cidrs(
|
|
101
211
|
neo4j_session: neo4j.Session,
|
|
102
|
-
|
|
212
|
+
requester_cidrs: List[Dict[str, Any]],
|
|
103
213
|
region: str,
|
|
104
214
|
aws_account_id: str,
|
|
105
215
|
update_tag: int,
|
|
106
216
|
) -> None:
|
|
217
|
+
load(
|
|
218
|
+
neo4j_session,
|
|
219
|
+
AWSIPv4CidrBlockSchema(),
|
|
220
|
+
requester_cidrs,
|
|
221
|
+
lastupdated=update_tag,
|
|
222
|
+
)
|
|
107
223
|
|
|
108
|
-
ingest_accepter_cidr = """
|
|
109
|
-
UNWIND $vpc_peerings AS vpc_peering
|
|
110
|
-
UNWIND vpc_peering.AccepterVpcInfo.CidrBlockSet AS c_b
|
|
111
|
-
|
|
112
|
-
MERGE (ac_b:AWSCidrBlock:AWSIpv4CidrBlock{id: vpc_peering.AccepterVpcInfo.VpcId + '|' + c_b.CidrBlock})
|
|
113
|
-
ON CREATE SET ac_b.firstseen = timestamp()
|
|
114
|
-
SET ac_b.lastupdated = $update_tag, ac_b.cidr_block = c_b.CidrBlock
|
|
115
|
-
|
|
116
|
-
WITH vpc_peering, ac_b
|
|
117
|
-
MATCH (pcx:AWSPeeringConnection{id: vpc_peering.VpcPeeringConnectionId}), (cb:AWSCidrBlock{id: ac_b.id})
|
|
118
|
-
MERGE (pcx)-[r:ACCEPTER_CIDR]->(cb)
|
|
119
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
120
|
-
SET r.lastupdated = $update_tag
|
|
121
|
-
|
|
122
|
-
WITH vpc_peering, ac_b
|
|
123
|
-
MATCH (vpc:AWSVpc{id: vpc_peering.AccepterVpcInfo.VpcId}), (cb:AWSCidrBlock{id: ac_b.id})
|
|
124
|
-
MERGE (vpc)-[r:BLOCK_ASSOCIATION]->(cb)
|
|
125
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
126
|
-
SET r.lastupdated = $update_tag
|
|
127
|
-
"""
|
|
128
224
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
225
|
+
@timeit
|
|
226
|
+
def load_aws_accounts_from_vpc_peering(
|
|
227
|
+
neo4j_session: neo4j.Session,
|
|
228
|
+
aws_accounts: List[Dict[str, Any]],
|
|
229
|
+
update_tag: int,
|
|
230
|
+
) -> None:
|
|
231
|
+
"""
|
|
232
|
+
Load AWS account nodes using the composite schema.
|
|
233
|
+
This allows VPC peering data to provide additional context about AWS accounts.
|
|
234
|
+
"""
|
|
235
|
+
load(
|
|
236
|
+
neo4j_session,
|
|
237
|
+
AWSAccountVPCPeeringSchema(),
|
|
238
|
+
aws_accounts,
|
|
239
|
+
lastupdated=update_tag,
|
|
135
240
|
)
|
|
136
241
|
|
|
137
242
|
|
|
138
243
|
@timeit
|
|
139
|
-
def
|
|
244
|
+
def load_vpc_nodes(
|
|
140
245
|
neo4j_session: neo4j.Session,
|
|
141
|
-
|
|
246
|
+
vpc_nodes: List[Dict[str, Any]],
|
|
142
247
|
region: str,
|
|
143
248
|
aws_account_id: str,
|
|
144
249
|
update_tag: int,
|
|
145
250
|
) -> None:
|
|
251
|
+
# Group VPC nodes by their actual account ID
|
|
252
|
+
vpc_nodes_by_account: Dict[str, List[Dict[str, Any]]] = {}
|
|
253
|
+
for vpc in vpc_nodes:
|
|
254
|
+
account_id = vpc.get(
|
|
255
|
+
"AccountId", aws_account_id
|
|
256
|
+
) # Use VPC's own account or fallback
|
|
257
|
+
if account_id not in vpc_nodes_by_account:
|
|
258
|
+
vpc_nodes_by_account[account_id] = []
|
|
259
|
+
vpc_nodes_by_account[account_id].append(vpc)
|
|
260
|
+
|
|
261
|
+
# Load VPCs for each account separately
|
|
262
|
+
for account_id, account_vpc_nodes in vpc_nodes_by_account.items():
|
|
263
|
+
# Remove the AccountId field as it's not part of the VPC schema
|
|
264
|
+
for vpc in account_vpc_nodes:
|
|
265
|
+
vpc.pop("AccountId", None)
|
|
266
|
+
|
|
267
|
+
load(
|
|
268
|
+
neo4j_session,
|
|
269
|
+
AWSVpcSchema(),
|
|
270
|
+
account_vpc_nodes,
|
|
271
|
+
lastupdated=update_tag,
|
|
272
|
+
AWS_ID=account_id,
|
|
273
|
+
Region=region,
|
|
274
|
+
)
|
|
146
275
|
|
|
147
|
-
ingest_requester_cidr = """
|
|
148
|
-
UNWIND $vpc_peerings AS vpc_peering
|
|
149
|
-
UNWIND vpc_peering.RequesterVpcInfo.CidrBlockSet AS c_b
|
|
150
|
-
|
|
151
|
-
MERGE (rc_b:AWSCidrBlock:AWSIpv4CidrBlock{id: vpc_peering.RequesterVpcInfo.VpcId + '|' + c_b.CidrBlock})
|
|
152
|
-
ON CREATE SET rc_b.firstseen = timestamp()
|
|
153
|
-
SET rc_b.lastupdated = $update_tag, rc_b.cidr_block = c_b.CidrBlock
|
|
154
|
-
|
|
155
|
-
WITH vpc_peering, rc_b
|
|
156
|
-
MATCH (pcx:AWSPeeringConnection{id: vpc_peering.VpcPeeringConnectionId}), (cb:AWSCidrBlock{id: rc_b.id})
|
|
157
|
-
MERGE (pcx)-[r:REQUESTER_CIDR]->(cb)
|
|
158
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
159
|
-
SET r.lastupdated = $update_tag
|
|
160
|
-
|
|
161
|
-
WITH vpc_peering, rc_b
|
|
162
|
-
MATCH (vpc:AWSVpc{id: vpc_peering.RequesterVpcInfo.VpcId}), (cb:AWSCidrBlock{id: rc_b.id})
|
|
163
|
-
MERGE (vpc)-[r:BLOCK_ASSOCIATION]->(cb)
|
|
164
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
165
|
-
SET r.lastupdated = $update_tag
|
|
166
|
-
"""
|
|
167
276
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
277
|
+
@timeit
|
|
278
|
+
def load_vpc_peerings(
|
|
279
|
+
neo4j_session: neo4j.Session,
|
|
280
|
+
vpc_peerings: List[Dict[str, Any]],
|
|
281
|
+
region: str,
|
|
282
|
+
aws_account_id: str,
|
|
283
|
+
update_tag: int,
|
|
284
|
+
) -> None:
|
|
285
|
+
load(
|
|
286
|
+
neo4j_session,
|
|
287
|
+
AWSPeeringConnectionSchema(),
|
|
288
|
+
vpc_peerings,
|
|
289
|
+
lastupdated=update_tag,
|
|
290
|
+
AWS_ID=aws_account_id,
|
|
174
291
|
)
|
|
175
292
|
|
|
176
293
|
|
|
@@ -179,11 +296,10 @@ def cleanup_vpc_peerings(
|
|
|
179
296
|
neo4j_session: neo4j.Session,
|
|
180
297
|
common_job_parameters: Dict,
|
|
181
298
|
) -> None:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
neo4j_session,
|
|
299
|
+
GraphJob.from_node_schema(
|
|
300
|
+
AWSPeeringConnectionSchema(),
|
|
185
301
|
common_job_parameters,
|
|
186
|
-
)
|
|
302
|
+
).run(neo4j_session)
|
|
187
303
|
|
|
188
304
|
|
|
189
305
|
@timeit
|
|
@@ -201,24 +317,45 @@ def sync_vpc_peerings(
|
|
|
201
317
|
region,
|
|
202
318
|
current_aws_account_id,
|
|
203
319
|
)
|
|
204
|
-
|
|
205
|
-
|
|
320
|
+
raw_data = get_vpc_peerings_data(boto3_session, region)
|
|
321
|
+
vpc_peerings, accepter_cidrs, requester_cidrs, vpc_nodes = (
|
|
322
|
+
transform_vpc_peering_data(
|
|
323
|
+
raw_data,
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
aws_accounts = transform_aws_accounts_from_vpc_peering(raw_data)
|
|
327
|
+
|
|
328
|
+
# Load AWS accounts first (composite pattern)
|
|
329
|
+
load_aws_accounts_from_vpc_peering(
|
|
330
|
+
neo4j_session,
|
|
331
|
+
aws_accounts,
|
|
332
|
+
update_tag,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
load_vpc_nodes(
|
|
206
336
|
neo4j_session,
|
|
207
|
-
|
|
337
|
+
vpc_nodes,
|
|
208
338
|
region,
|
|
209
339
|
current_aws_account_id,
|
|
210
340
|
update_tag,
|
|
211
341
|
)
|
|
212
342
|
load_accepter_cidrs(
|
|
213
343
|
neo4j_session,
|
|
214
|
-
|
|
344
|
+
accepter_cidrs,
|
|
215
345
|
region,
|
|
216
346
|
current_aws_account_id,
|
|
217
347
|
update_tag,
|
|
218
348
|
)
|
|
219
349
|
load_requester_cidrs(
|
|
220
350
|
neo4j_session,
|
|
221
|
-
|
|
351
|
+
requester_cidrs,
|
|
352
|
+
region,
|
|
353
|
+
current_aws_account_id,
|
|
354
|
+
update_tag,
|
|
355
|
+
)
|
|
356
|
+
load_vpc_peerings(
|
|
357
|
+
neo4j_session,
|
|
358
|
+
vpc_peerings,
|
|
222
359
|
region,
|
|
223
360
|
current_aws_account_id,
|
|
224
361
|
update_tag,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Dict
|
|
3
3
|
from typing import List
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import neo4j
|
|
7
6
|
|
|
@@ -29,28 +28,28 @@ def _sync_one_subscription(
|
|
|
29
28
|
) -> None:
|
|
30
29
|
compute.sync(
|
|
31
30
|
neo4j_session,
|
|
32
|
-
credentials.
|
|
31
|
+
credentials.credential,
|
|
33
32
|
subscription_id,
|
|
34
33
|
update_tag,
|
|
35
34
|
common_job_parameters,
|
|
36
35
|
)
|
|
37
36
|
cosmosdb.sync(
|
|
38
37
|
neo4j_session,
|
|
39
|
-
credentials.
|
|
38
|
+
credentials.credential,
|
|
40
39
|
subscription_id,
|
|
41
40
|
update_tag,
|
|
42
41
|
common_job_parameters,
|
|
43
42
|
)
|
|
44
43
|
sql.sync(
|
|
45
44
|
neo4j_session,
|
|
46
|
-
credentials.
|
|
45
|
+
credentials.credential,
|
|
47
46
|
subscription_id,
|
|
48
47
|
update_tag,
|
|
49
48
|
common_job_parameters,
|
|
50
49
|
)
|
|
51
50
|
storage.sync(
|
|
52
51
|
neo4j_session,
|
|
53
|
-
credentials.
|
|
52
|
+
credentials.credential,
|
|
54
53
|
subscription_id,
|
|
55
54
|
update_tag,
|
|
56
55
|
common_job_parameters,
|
|
@@ -59,13 +58,12 @@ def _sync_one_subscription(
|
|
|
59
58
|
|
|
60
59
|
def _sync_tenant(
|
|
61
60
|
neo4j_session: neo4j.Session,
|
|
62
|
-
|
|
63
|
-
current_user: Optional[str],
|
|
61
|
+
credentials: Credentials,
|
|
64
62
|
update_tag: int,
|
|
65
63
|
common_job_parameters: Dict,
|
|
66
64
|
) -> None:
|
|
67
|
-
logger.info("Syncing Azure Tenant: %s", tenant_id)
|
|
68
|
-
tenant.sync(neo4j_session, tenant_id,
|
|
65
|
+
logger.info("Syncing Azure Tenant: %s", credentials.tenant_id)
|
|
66
|
+
tenant.sync(neo4j_session, credentials.tenant_id, None, update_tag, common_job_parameters) # type: ignore
|
|
69
67
|
|
|
70
68
|
|
|
71
69
|
def _sync_multiple_subscriptions(
|
|
@@ -122,40 +120,45 @@ def start_azure_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
122
120
|
logger.error(
|
|
123
121
|
(
|
|
124
122
|
"Unable to authenticate with Azure Service Principal, an error occurred: %s."
|
|
125
|
-
"Make sure your
|
|
123
|
+
"Make sure your credentials (CLI or Service Principal) are configured correctly."
|
|
126
124
|
),
|
|
127
125
|
e,
|
|
128
126
|
)
|
|
129
127
|
return
|
|
130
128
|
|
|
129
|
+
if not credentials:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
common_job_parameters["TENANT_ID"] = credentials.tenant_id
|
|
133
|
+
|
|
131
134
|
_sync_tenant(
|
|
132
135
|
neo4j_session,
|
|
133
|
-
credentials
|
|
134
|
-
credentials.get_current_user(),
|
|
136
|
+
credentials,
|
|
135
137
|
config.update_tag,
|
|
136
138
|
common_job_parameters,
|
|
137
139
|
)
|
|
140
|
+
if credentials.tenant_id:
|
|
141
|
+
if config.azure_sync_all_subscriptions:
|
|
142
|
+
subscriptions = subscription.get_all_azure_subscriptions(credentials)
|
|
138
143
|
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
else:
|
|
145
|
+
sub_id_to_sync = config.azure_subscription_id or credentials.subscription_id
|
|
146
|
+
subscriptions = subscription.get_current_azure_subscription(
|
|
147
|
+
credentials,
|
|
148
|
+
sub_id_to_sync,
|
|
149
|
+
)
|
|
141
150
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
151
|
+
if not subscriptions:
|
|
152
|
+
logger.warning(
|
|
153
|
+
"No valid Azure credentials are found. No Azure subscriptions can be synced. Exiting Azure sync stage.",
|
|
154
|
+
)
|
|
155
|
+
return
|
|
147
156
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
_sync_multiple_subscriptions(
|
|
158
|
+
neo4j_session,
|
|
159
|
+
credentials,
|
|
160
|
+
credentials.tenant_id,
|
|
161
|
+
subscriptions,
|
|
162
|
+
config.update_tag,
|
|
163
|
+
common_job_parameters,
|
|
151
164
|
)
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
_sync_multiple_subscriptions(
|
|
155
|
-
neo4j_session,
|
|
156
|
-
credentials,
|
|
157
|
-
credentials.get_tenant_id(),
|
|
158
|
-
subscriptions,
|
|
159
|
-
config.update_tag,
|
|
160
|
-
common_job_parameters,
|
|
161
|
-
)
|
|
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
|
|
18
18
|
def get_all_azure_subscriptions(credentials: Credentials) -> List[Dict]:
|
|
19
19
|
try:
|
|
20
20
|
# Create the client
|
|
21
|
-
client = SubscriptionClient(credentials.
|
|
21
|
+
client = SubscriptionClient(credentials.credential)
|
|
22
22
|
|
|
23
23
|
# Get all the accessible subscriptions
|
|
24
24
|
subs = list(client.subscriptions.list())
|
|
@@ -52,7 +52,7 @@ def get_current_azure_subscription(
|
|
|
52
52
|
) -> List[Dict]:
|
|
53
53
|
try:
|
|
54
54
|
# Create the client
|
|
55
|
-
client = SubscriptionClient(credentials.
|
|
55
|
+
client = SubscriptionClient(credentials.credential)
|
|
56
56
|
|
|
57
57
|
# Get all the accessible subscriptions
|
|
58
58
|
sub = client.subscriptions.get(subscription_id)
|