cartography 0.110.0rc1__py3-none-any.whl → 0.111.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 +16 -3
- cartography/cli.py +46 -8
- cartography/config.py +16 -9
- cartography/data/indexes.cypher +0 -2
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/graph/querybuilder.py +70 -0
- cartography/intel/aws/apigateway.py +113 -4
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/ec2/vpc.py +140 -124
- cartography/intel/aws/ecs.py +7 -1
- cartography/intel/aws/eventbridge.py +73 -0
- 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 +32 -48
- 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 +4 -0
- cartography/intel/kubernetes/rbac.py +464 -0
- cartography/intel/kubernetes/util.py +17 -0
- cartography/models/aws/apigateway/apigatewaydeployment.py +74 -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/ec2/vpc.py +46 -0
- cartography/models/aws/ec2/vpc_cidr.py +102 -0
- cartography/models/aws/ecs/services.py +17 -0
- cartography/models/aws/ecs/tasks.py +1 -0
- cartography/models/aws/eventbridge/target.py +71 -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/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 +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/models/tailscale/device.py +1 -0
- cartography/sync.py +2 -0
- cartography/util.py +8 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/METADATA +4 -3
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/RECORD +85 -46
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
- cartography/intel/entra/resources.py +0 -20
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- /cartography/models/aws/{__init__.py → apigateway/__init__.py} +0 -0
- /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
- /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
- /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
- /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/WHEEL +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
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.cognito.identity_pool import CognitoIdentityPoolSchema
|
|
13
|
+
from cartography.models.aws.cognito.user_pool import CognitoUserPoolSchema
|
|
14
|
+
from cartography.util import aws_handle_regions
|
|
15
|
+
from cartography.util import timeit
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@timeit
|
|
21
|
+
@aws_handle_regions
|
|
22
|
+
def get_identity_pools(
|
|
23
|
+
boto3_session: boto3.Session, region: str
|
|
24
|
+
) -> List[Dict[str, Any]]:
|
|
25
|
+
client = boto3_session.client(
|
|
26
|
+
"cognito-identity", region_name=region, config=get_botocore_config()
|
|
27
|
+
)
|
|
28
|
+
paginator = client.get_paginator("list_identity_pools")
|
|
29
|
+
|
|
30
|
+
all_identity_pools = []
|
|
31
|
+
|
|
32
|
+
for page in paginator.paginate(MaxResults=50):
|
|
33
|
+
identity_pools = page.get("IdentityPools", [])
|
|
34
|
+
all_identity_pools.extend(identity_pools)
|
|
35
|
+
return all_identity_pools
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@timeit
|
|
39
|
+
@aws_handle_regions
|
|
40
|
+
def get_identity_pool_roles(
|
|
41
|
+
boto3_session: boto3.Session, identity_pools: List[Dict[str, Any]], region: str
|
|
42
|
+
) -> List[Dict[str, Any]]:
|
|
43
|
+
client = boto3_session.client(
|
|
44
|
+
"cognito-identity", region_name=region, config=get_botocore_config()
|
|
45
|
+
)
|
|
46
|
+
all_identity_pool_details = []
|
|
47
|
+
for identity_pool in identity_pools:
|
|
48
|
+
response = client.get_identity_pool_roles(
|
|
49
|
+
IdentityPoolId=identity_pool["IdentityPoolId"]
|
|
50
|
+
)
|
|
51
|
+
all_identity_pool_details.append(response)
|
|
52
|
+
return all_identity_pool_details
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@timeit
|
|
56
|
+
@aws_handle_regions
|
|
57
|
+
def get_user_pools(boto3_session: boto3.Session, region: str) -> List[Dict[str, Any]]:
|
|
58
|
+
client = boto3_session.client(
|
|
59
|
+
"cognito-idp", region_name=region, config=get_botocore_config()
|
|
60
|
+
)
|
|
61
|
+
paginator = client.get_paginator("list_user_pools")
|
|
62
|
+
all_user_pools = []
|
|
63
|
+
|
|
64
|
+
for page in paginator.paginate(MaxResults=50):
|
|
65
|
+
user_pools = page.get("UserPools", [])
|
|
66
|
+
all_user_pools.extend(user_pools)
|
|
67
|
+
return all_user_pools
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def transform_identity_pools(
|
|
71
|
+
identity_pools: List[Dict[str, Any]], region: str
|
|
72
|
+
) -> List[Dict[str, Any]]:
|
|
73
|
+
transformed_identity_pools = []
|
|
74
|
+
for pool in identity_pools:
|
|
75
|
+
transformed_pool = {
|
|
76
|
+
"IdentityPoolId": pool["IdentityPoolId"],
|
|
77
|
+
"Region": region,
|
|
78
|
+
"Roles": list(pool.get("Roles", {}).values()),
|
|
79
|
+
}
|
|
80
|
+
transformed_identity_pools.append(transformed_pool)
|
|
81
|
+
return transformed_identity_pools
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def transform_user_pools(
|
|
85
|
+
user_pools: List[Dict[str, Any]], region: str
|
|
86
|
+
) -> List[Dict[str, Any]]:
|
|
87
|
+
transformed_user_pools = []
|
|
88
|
+
for pool in user_pools:
|
|
89
|
+
transformed_pool = {
|
|
90
|
+
"Id": pool["Id"],
|
|
91
|
+
"Region": region,
|
|
92
|
+
"Name": pool["Name"],
|
|
93
|
+
"Status": pool.get("Status"),
|
|
94
|
+
}
|
|
95
|
+
transformed_user_pools.append(transformed_pool)
|
|
96
|
+
return transformed_user_pools
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@timeit
|
|
100
|
+
def load_identity_pools(
|
|
101
|
+
neo4j_session: neo4j.Session,
|
|
102
|
+
data: List[Dict[str, Any]],
|
|
103
|
+
region: str,
|
|
104
|
+
current_aws_account_id: str,
|
|
105
|
+
aws_update_tag: int,
|
|
106
|
+
) -> None:
|
|
107
|
+
logger.info(
|
|
108
|
+
f"Loading Cognito Identity Pools {len(data)} for region '{region}' into graph.",
|
|
109
|
+
)
|
|
110
|
+
load(
|
|
111
|
+
neo4j_session,
|
|
112
|
+
CognitoIdentityPoolSchema(),
|
|
113
|
+
data,
|
|
114
|
+
lastupdated=aws_update_tag,
|
|
115
|
+
Region=region,
|
|
116
|
+
AWS_ID=current_aws_account_id,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@timeit
|
|
121
|
+
def load_user_pools(
|
|
122
|
+
neo4j_session: neo4j.Session,
|
|
123
|
+
data: List[Dict[str, Any]],
|
|
124
|
+
region: str,
|
|
125
|
+
current_aws_account_id: str,
|
|
126
|
+
aws_update_tag: int,
|
|
127
|
+
) -> None:
|
|
128
|
+
logger.info(
|
|
129
|
+
f"Loading Cognito User Pools {len(data)} for region '{region}' into graph.",
|
|
130
|
+
)
|
|
131
|
+
load(
|
|
132
|
+
neo4j_session,
|
|
133
|
+
CognitoUserPoolSchema(),
|
|
134
|
+
data,
|
|
135
|
+
lastupdated=aws_update_tag,
|
|
136
|
+
Region=region,
|
|
137
|
+
AWS_ID=current_aws_account_id,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@timeit
|
|
142
|
+
def cleanup(
|
|
143
|
+
neo4j_session: neo4j.Session,
|
|
144
|
+
common_job_parameters: Dict[str, Any],
|
|
145
|
+
) -> None:
|
|
146
|
+
logger.debug("Running Efs cleanup job.")
|
|
147
|
+
GraphJob.from_node_schema(CognitoIdentityPoolSchema(), common_job_parameters).run(
|
|
148
|
+
neo4j_session
|
|
149
|
+
)
|
|
150
|
+
GraphJob.from_node_schema(CognitoUserPoolSchema(), common_job_parameters).run(
|
|
151
|
+
neo4j_session
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@timeit
|
|
156
|
+
def sync(
|
|
157
|
+
neo4j_session: neo4j.Session,
|
|
158
|
+
boto3_session: boto3.session.Session,
|
|
159
|
+
regions: List[str],
|
|
160
|
+
current_aws_account_id: str,
|
|
161
|
+
update_tag: int,
|
|
162
|
+
common_job_parameters: Dict[str, Any],
|
|
163
|
+
) -> None:
|
|
164
|
+
for region in regions:
|
|
165
|
+
logger.info(
|
|
166
|
+
f"Syncing Cognito Identity Pools for region '{region}' in account '{current_aws_account_id}'.",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
identity_pools = get_identity_pools(boto3_session, region)
|
|
170
|
+
if not identity_pools:
|
|
171
|
+
logger.info(
|
|
172
|
+
f"No Cognito Identity Pools found in region '{region}'. Skipping sync."
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
identity_pool_roles = get_identity_pool_roles(
|
|
176
|
+
boto3_session, identity_pools, region
|
|
177
|
+
)
|
|
178
|
+
transformed_identity_pools = transform_identity_pools(
|
|
179
|
+
identity_pool_roles, region
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
load_identity_pools(
|
|
183
|
+
neo4j_session,
|
|
184
|
+
transformed_identity_pools,
|
|
185
|
+
region,
|
|
186
|
+
current_aws_account_id,
|
|
187
|
+
update_tag,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
user_pools = get_user_pools(boto3_session, region)
|
|
191
|
+
transformed_user_pools = transform_user_pools(user_pools, region)
|
|
192
|
+
|
|
193
|
+
load_user_pools(
|
|
194
|
+
neo4j_session,
|
|
195
|
+
transformed_user_pools,
|
|
196
|
+
region,
|
|
197
|
+
current_aws_account_id,
|
|
198
|
+
update_tag,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
cleanup(neo4j_session, common_job_parameters)
|
cartography/intel/aws/ec2/vpc.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
3
|
-
from typing import Dict
|
|
4
|
-
from typing import List
|
|
2
|
+
from typing import Any
|
|
5
3
|
|
|
6
4
|
import boto3
|
|
7
5
|
import neo4j
|
|
8
6
|
|
|
7
|
+
from cartography.client.core.tx import load
|
|
8
|
+
from cartography.graph.job import GraphJob
|
|
9
|
+
from cartography.models.aws.ec2.vpc import AWSVpcSchema
|
|
10
|
+
from cartography.models.aws.ec2.vpc_cidr import AWSIPv4CidrBlockSchema
|
|
11
|
+
from cartography.models.aws.ec2.vpc_cidr import AWSIPv6CidrBlockSchema
|
|
9
12
|
from cartography.util import aws_handle_regions
|
|
10
|
-
from cartography.util import run_cleanup_job
|
|
11
13
|
from cartography.util import timeit
|
|
12
14
|
|
|
13
15
|
from .util import get_botocore_config
|
|
@@ -17,87 +19,78 @@ logger = logging.getLogger(__name__)
|
|
|
17
19
|
|
|
18
20
|
@timeit
|
|
19
21
|
@aws_handle_regions
|
|
20
|
-
def get_ec2_vpcs(
|
|
22
|
+
def get_ec2_vpcs(
|
|
23
|
+
boto3_session: boto3.session.Session,
|
|
24
|
+
region: str,
|
|
25
|
+
) -> list[dict[str, Any]]:
|
|
21
26
|
client = boto3_session.client(
|
|
22
27
|
"ec2",
|
|
23
28
|
region_name=region,
|
|
24
29
|
config=get_botocore_config(),
|
|
25
30
|
)
|
|
26
|
-
return client.describe_vpcs()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ingest_statement = _get_cidr_association_statement(block_type)
|
|
79
|
-
|
|
80
|
-
if block_type == "ipv6":
|
|
81
|
-
data = vpc_data.get("Ipv6CidrBlockAssociationSet", [])
|
|
82
|
-
else:
|
|
83
|
-
data = vpc_data.get("CidrBlockAssociationSet", [])
|
|
84
|
-
|
|
85
|
-
neo4j_session.run(
|
|
86
|
-
ingest_statement,
|
|
87
|
-
VpcId=vpc_id,
|
|
88
|
-
CidrBlock=data,
|
|
89
|
-
update_tag=update_tag,
|
|
90
|
-
)
|
|
31
|
+
return client.describe_vpcs().get("Vpcs", [])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def transform_vpc_data(
|
|
35
|
+
vpc_list: list[dict[str, Any]], region: str
|
|
36
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
|
|
37
|
+
|
|
38
|
+
vpc_data: list[dict[str, Any]] = []
|
|
39
|
+
ipv4_cidr_blocks: list[dict[str, Any]] = []
|
|
40
|
+
ipv6_cidr_blocks: list[dict[str, Any]] = []
|
|
41
|
+
|
|
42
|
+
for vpc in vpc_list:
|
|
43
|
+
vpc_record = {
|
|
44
|
+
"VpcId": vpc.get("VpcId"),
|
|
45
|
+
"InstanceTenancy": vpc.get("InstanceTenancy"),
|
|
46
|
+
"State": vpc.get("State"),
|
|
47
|
+
"IsDefault": vpc.get("IsDefault"),
|
|
48
|
+
"PrimaryCIDRBlock": vpc.get("CidrBlock"),
|
|
49
|
+
"DhcpOptionsId": vpc.get("DhcpOptionsId"),
|
|
50
|
+
"lastupdated": vpc.get("lastupdated"),
|
|
51
|
+
}
|
|
52
|
+
vpc_data.append(vpc_record)
|
|
53
|
+
|
|
54
|
+
ipv4_associations = vpc.get("CidrBlockAssociationSet", [])
|
|
55
|
+
for association in ipv4_associations:
|
|
56
|
+
ipv4_block = {
|
|
57
|
+
"Id": vpc["VpcId"] + "|" + association.get("CidrBlock"),
|
|
58
|
+
"VpcId": vpc["VpcId"],
|
|
59
|
+
"AssociationId": association.get("AssociationId"),
|
|
60
|
+
"CidrBlock": association.get("CidrBlock"),
|
|
61
|
+
"BlockState": association.get("CidrBlockState", {}).get("State"),
|
|
62
|
+
"BlockStateMessage": association.get("CidrBlockState", {}).get(
|
|
63
|
+
"StatusMessage"
|
|
64
|
+
),
|
|
65
|
+
}
|
|
66
|
+
ipv4_cidr_blocks.append(ipv4_block)
|
|
67
|
+
|
|
68
|
+
ipv6_associations = vpc.get("Ipv6CidrBlockAssociationSet", [])
|
|
69
|
+
for association in ipv6_associations:
|
|
70
|
+
ipv6_block = {
|
|
71
|
+
"Id": vpc["VpcId"] + "|" + association.get("Ipv6CidrBlock"),
|
|
72
|
+
"VpcId": vpc["VpcId"],
|
|
73
|
+
"AssociationId": association.get("AssociationId"),
|
|
74
|
+
"CidrBlock": association.get("Ipv6CidrBlock"),
|
|
75
|
+
"BlockState": association.get("Ipv6CidrBlockState", {}).get("State"),
|
|
76
|
+
"BlockStateMessage": association.get("Ipv6CidrBlockState", {}).get(
|
|
77
|
+
"StatusMessage"
|
|
78
|
+
),
|
|
79
|
+
}
|
|
80
|
+
ipv6_cidr_blocks.append(ipv6_block)
|
|
81
|
+
|
|
82
|
+
return vpc_data, ipv4_cidr_blocks, ipv6_cidr_blocks
|
|
91
83
|
|
|
92
84
|
|
|
93
85
|
@timeit
|
|
94
86
|
def load_ec2_vpcs(
|
|
95
87
|
neo4j_session: neo4j.Session,
|
|
96
|
-
|
|
88
|
+
vpcs: list[dict[str, Any]],
|
|
97
89
|
region: str,
|
|
98
|
-
|
|
90
|
+
aws_account_id: str,
|
|
99
91
|
update_tag: int,
|
|
100
92
|
) -> None:
|
|
93
|
+
logger.info(f"Loading {len(vpcs)} EC2 VPCs for region '{region}' into graph.")
|
|
101
94
|
# https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-vpcs.html
|
|
102
95
|
# {
|
|
103
96
|
# "Vpcs": [
|
|
@@ -126,69 +119,69 @@ def load_ec2_vpcs(
|
|
|
126
119
|
# }
|
|
127
120
|
# ]
|
|
128
121
|
# }
|
|
122
|
+
load(
|
|
123
|
+
neo4j_session,
|
|
124
|
+
AWSVpcSchema(),
|
|
125
|
+
vpcs,
|
|
126
|
+
lastupdated=update_tag,
|
|
127
|
+
Region=region,
|
|
128
|
+
AWS_ID=aws_account_id,
|
|
129
|
+
)
|
|
129
130
|
|
|
130
|
-
ingest_vpc = """
|
|
131
|
-
MERGE (new_vpc:AWSVpc{id: $VpcId})
|
|
132
|
-
ON CREATE SET new_vpc.firstseen = timestamp(), new_vpc.vpcid =$VpcId
|
|
133
|
-
SET new_vpc.instance_tenancy = $InstanceTenancy,
|
|
134
|
-
new_vpc.state = $State,
|
|
135
|
-
new_vpc.is_default = $IsDefault,
|
|
136
|
-
new_vpc.primary_cidr_block = $PrimaryCIDRBlock,
|
|
137
|
-
new_vpc.dhcp_options_id = $DhcpOptionsId,
|
|
138
|
-
new_vpc.region = $Region,
|
|
139
|
-
new_vpc.lastupdated = $update_tag
|
|
140
|
-
WITH new_vpc
|
|
141
|
-
MATCH (awsAccount:AWSAccount{id: $AWS_ACCOUNT_ID})
|
|
142
|
-
MERGE (awsAccount)-[r:RESOURCE]->(new_vpc)
|
|
143
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
144
|
-
SET r.lastupdated = $update_tag"""
|
|
145
|
-
|
|
146
|
-
for vpc in data:
|
|
147
|
-
vpc_id = vpc["VpcId"] # fail if not present
|
|
148
|
-
|
|
149
|
-
neo4j_session.run(
|
|
150
|
-
ingest_vpc,
|
|
151
|
-
VpcId=vpc_id,
|
|
152
|
-
InstanceTenancy=vpc.get("InstanceTenancy", None),
|
|
153
|
-
State=vpc.get("State", None),
|
|
154
|
-
IsDefault=vpc.get("IsDefault", None),
|
|
155
|
-
PrimaryCIDRBlock=vpc.get("CidrBlock", None),
|
|
156
|
-
DhcpOptionsId=vpc.get("DhcpOptionsId", None),
|
|
157
|
-
Region=region,
|
|
158
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
159
|
-
update_tag=update_tag,
|
|
160
|
-
)
|
|
161
131
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
132
|
+
@timeit
|
|
133
|
+
def load_ipv4_cidr_blocks(
|
|
134
|
+
neo4j_session: neo4j.Session,
|
|
135
|
+
ipv4_cidr_blocks: list[dict[str, Any]],
|
|
136
|
+
region: str,
|
|
137
|
+
aws_account_id: str,
|
|
138
|
+
update_tag: int,
|
|
139
|
+
) -> None:
|
|
140
|
+
load(
|
|
141
|
+
neo4j_session,
|
|
142
|
+
AWSIPv4CidrBlockSchema(),
|
|
143
|
+
ipv4_cidr_blocks,
|
|
144
|
+
lastupdated=update_tag,
|
|
145
|
+
)
|
|
169
146
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
147
|
+
|
|
148
|
+
@timeit
|
|
149
|
+
def load_ipv6_cidr_blocks(
|
|
150
|
+
neo4j_session: neo4j.Session,
|
|
151
|
+
ipv6_cidr_blocks: list[dict[str, Any]],
|
|
152
|
+
region: str,
|
|
153
|
+
aws_account_id: str,
|
|
154
|
+
update_tag: int,
|
|
155
|
+
) -> None:
|
|
156
|
+
load(
|
|
157
|
+
neo4j_session,
|
|
158
|
+
AWSIPv6CidrBlockSchema(),
|
|
159
|
+
ipv6_cidr_blocks,
|
|
160
|
+
lastupdated=update_tag,
|
|
161
|
+
)
|
|
177
162
|
|
|
178
163
|
|
|
179
164
|
@timeit
|
|
180
|
-
def
|
|
181
|
-
|
|
165
|
+
def cleanup(
|
|
166
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
167
|
+
) -> None:
|
|
168
|
+
GraphJob.from_node_schema(AWSIPv6CidrBlockSchema(), common_job_parameters).run(
|
|
169
|
+
neo4j_session
|
|
170
|
+
)
|
|
171
|
+
GraphJob.from_node_schema(AWSIPv4CidrBlockSchema(), common_job_parameters).run(
|
|
172
|
+
neo4j_session
|
|
173
|
+
)
|
|
174
|
+
GraphJob.from_node_schema(AWSVpcSchema(), common_job_parameters).run(neo4j_session)
|
|
182
175
|
|
|
183
176
|
|
|
184
177
|
@timeit
|
|
185
178
|
def sync_vpc(
|
|
186
179
|
neo4j_session: neo4j.Session,
|
|
187
180
|
boto3_session: boto3.session.Session,
|
|
188
|
-
regions:
|
|
181
|
+
regions: list[str],
|
|
189
182
|
current_aws_account_id: str,
|
|
190
183
|
update_tag: int,
|
|
191
|
-
common_job_parameters:
|
|
184
|
+
common_job_parameters: dict[str, Any],
|
|
192
185
|
) -> None:
|
|
193
186
|
for region in regions:
|
|
194
187
|
logger.info(
|
|
@@ -196,6 +189,29 @@ def sync_vpc(
|
|
|
196
189
|
region,
|
|
197
190
|
current_aws_account_id,
|
|
198
191
|
)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
raw_vpc_data = get_ec2_vpcs(boto3_session, region)
|
|
193
|
+
vpc_data, ipv4_cidr_blocks, ipv6_cidr_blocks = transform_vpc_data(
|
|
194
|
+
raw_vpc_data, region
|
|
195
|
+
)
|
|
196
|
+
load_ec2_vpcs(
|
|
197
|
+
neo4j_session,
|
|
198
|
+
vpc_data,
|
|
199
|
+
region,
|
|
200
|
+
current_aws_account_id,
|
|
201
|
+
update_tag,
|
|
202
|
+
)
|
|
203
|
+
load_ipv4_cidr_blocks(
|
|
204
|
+
neo4j_session,
|
|
205
|
+
ipv4_cidr_blocks,
|
|
206
|
+
region,
|
|
207
|
+
current_aws_account_id,
|
|
208
|
+
update_tag,
|
|
209
|
+
)
|
|
210
|
+
load_ipv6_cidr_blocks(
|
|
211
|
+
neo4j_session,
|
|
212
|
+
ipv6_cidr_blocks,
|
|
213
|
+
region,
|
|
214
|
+
current_aws_account_id,
|
|
215
|
+
update_tag,
|
|
216
|
+
)
|
|
217
|
+
cleanup(neo4j_session, common_job_parameters)
|
cartography/intel/aws/ecs.py
CHANGED
|
@@ -171,9 +171,15 @@ def _get_containers_from_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, An
|
|
|
171
171
|
|
|
172
172
|
def transform_ecs_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
173
173
|
"""
|
|
174
|
-
Extract network interface ID from task attachments.
|
|
174
|
+
Extract network interface ID from task attachments and service name from group.
|
|
175
175
|
"""
|
|
176
176
|
for task in tasks:
|
|
177
|
+
# Extract serviceName from group field
|
|
178
|
+
group = task.get("group")
|
|
179
|
+
if group and group.startswith("service:"):
|
|
180
|
+
task["serviceName"] = group.split("service:", 1)[1]
|
|
181
|
+
|
|
182
|
+
# Extract network interface ID from task attachments
|
|
177
183
|
for attachment in task.get("attachments", []):
|
|
178
184
|
if attachment.get("type") == "ElasticNetworkInterface":
|
|
179
185
|
details = attachment.get("details", [])
|
|
@@ -10,6 +10,7 @@ from cartography.client.core.tx import load
|
|
|
10
10
|
from cartography.graph.job import GraphJob
|
|
11
11
|
from cartography.intel.aws.ec2.util import get_botocore_config
|
|
12
12
|
from cartography.models.aws.eventbridge.rule import EventBridgeRuleSchema
|
|
13
|
+
from cartography.models.aws.eventbridge.target import EventBridgeTargetSchema
|
|
13
14
|
from cartography.util import aws_handle_regions
|
|
14
15
|
from cartography.util import timeit
|
|
15
16
|
|
|
@@ -33,6 +34,44 @@ def get_eventbridge_rules(
|
|
|
33
34
|
return rules
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
@timeit
|
|
38
|
+
@aws_handle_regions
|
|
39
|
+
def get_eventbridge_targets(
|
|
40
|
+
boto3_session: boto3.Session, region: str, rules: List[Dict[str, Any]]
|
|
41
|
+
) -> List[Dict[str, Any]]:
|
|
42
|
+
client = boto3_session.client(
|
|
43
|
+
"events", region_name=region, config=get_botocore_config()
|
|
44
|
+
)
|
|
45
|
+
targets = []
|
|
46
|
+
for rule in rules:
|
|
47
|
+
paginator = client.get_paginator("list_targets_by_rule")
|
|
48
|
+
for page in paginator.paginate(Rule=rule["Name"]):
|
|
49
|
+
for target in page.get("Targets", []):
|
|
50
|
+
target["RuleArn"] = rule["Arn"]
|
|
51
|
+
targets.append(target)
|
|
52
|
+
return targets
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def transform_eventbridge_targets(
|
|
56
|
+
targets: List[Dict[str, Any]],
|
|
57
|
+
region: str,
|
|
58
|
+
) -> List[Dict[str, Any]]:
|
|
59
|
+
"""
|
|
60
|
+
Transform EventBridge target data for ingestion into Neo4j.
|
|
61
|
+
"""
|
|
62
|
+
transformed_data = []
|
|
63
|
+
for target in targets:
|
|
64
|
+
transformed_target = {
|
|
65
|
+
"Id": target["Arn"],
|
|
66
|
+
"Arn": target["Arn"],
|
|
67
|
+
"RuleArn": target["RuleArn"],
|
|
68
|
+
"RoleArn": target.get("RoleArn"),
|
|
69
|
+
"Region": region,
|
|
70
|
+
}
|
|
71
|
+
transformed_data.append(transformed_target)
|
|
72
|
+
return transformed_data
|
|
73
|
+
|
|
74
|
+
|
|
36
75
|
@timeit
|
|
37
76
|
def load_eventbridge_rules(
|
|
38
77
|
neo4j_session: neo4j.Session,
|
|
@@ -54,6 +93,27 @@ def load_eventbridge_rules(
|
|
|
54
93
|
)
|
|
55
94
|
|
|
56
95
|
|
|
96
|
+
@timeit
|
|
97
|
+
def load_eventbridge_targets(
|
|
98
|
+
neo4j_session: neo4j.Session,
|
|
99
|
+
data: List[Dict[str, Any]],
|
|
100
|
+
region: str,
|
|
101
|
+
current_aws_account_id: str,
|
|
102
|
+
aws_update_tag: int,
|
|
103
|
+
) -> None:
|
|
104
|
+
logger.info(
|
|
105
|
+
f"Loading EventBridge {len(data)} targets for region '{region}' into graph.",
|
|
106
|
+
)
|
|
107
|
+
load(
|
|
108
|
+
neo4j_session,
|
|
109
|
+
EventBridgeTargetSchema(),
|
|
110
|
+
data,
|
|
111
|
+
lastupdated=aws_update_tag,
|
|
112
|
+
Region=region,
|
|
113
|
+
AWS_ID=current_aws_account_id,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
57
117
|
@timeit
|
|
58
118
|
def cleanup(
|
|
59
119
|
neo4j_session: neo4j.Session,
|
|
@@ -63,6 +123,9 @@ def cleanup(
|
|
|
63
123
|
GraphJob.from_node_schema(EventBridgeRuleSchema(), common_job_parameters).run(
|
|
64
124
|
neo4j_session
|
|
65
125
|
)
|
|
126
|
+
GraphJob.from_node_schema(EventBridgeTargetSchema(), common_job_parameters).run(
|
|
127
|
+
neo4j_session
|
|
128
|
+
)
|
|
66
129
|
|
|
67
130
|
|
|
68
131
|
@timeit
|
|
@@ -88,4 +151,14 @@ def sync(
|
|
|
88
151
|
update_tag,
|
|
89
152
|
)
|
|
90
153
|
|
|
154
|
+
targets = get_eventbridge_targets(boto3_session, region, rules)
|
|
155
|
+
transformed_targets = transform_eventbridge_targets(targets, region)
|
|
156
|
+
load_eventbridge_targets(
|
|
157
|
+
neo4j_session,
|
|
158
|
+
transformed_targets,
|
|
159
|
+
region,
|
|
160
|
+
current_aws_account_id,
|
|
161
|
+
update_tag,
|
|
162
|
+
)
|
|
163
|
+
|
|
91
164
|
cleanup(neo4j_session, common_job_parameters)
|