@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39
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.
- package/README.md +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Organizations Store - Persistência em JSON
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
|
|
9
|
+
class OrganizationsStore {
|
|
10
|
+
constructor(dataDir) {
|
|
11
|
+
this.dataDir = dataDir;
|
|
12
|
+
this.organizationsDir = path.join(dataDir, 'organizations');
|
|
13
|
+
this.metadataFile = path.join(this.organizationsDir, '__metadata__.json');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(this.organizationsDir)) {
|
|
16
|
+
fs.mkdirSync(this.organizationsDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Carrega metadados da organização
|
|
22
|
+
*/
|
|
23
|
+
loadOrganizationMetadata() {
|
|
24
|
+
if (fs.existsSync(this.metadataFile)) {
|
|
25
|
+
try {
|
|
26
|
+
const data = fs.readFileSync(this.metadataFile, 'utf8');
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Salva metadados da organização
|
|
38
|
+
*/
|
|
39
|
+
saveOrganizationMetadata(metadata) {
|
|
40
|
+
try {
|
|
41
|
+
fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
|
|
42
|
+
logger.debug(`Metadados da organização salvos em ${this.metadataFile}`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Carrega OUs
|
|
50
|
+
*/
|
|
51
|
+
loadOrganizationalUnits() {
|
|
52
|
+
const ouFile = path.join(this.organizationsDir, '__ous__.json');
|
|
53
|
+
|
|
54
|
+
if (fs.existsSync(ouFile)) {
|
|
55
|
+
try {
|
|
56
|
+
const data = fs.readFileSync(ouFile, 'utf8');
|
|
57
|
+
return JSON.parse(data);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.warn(`Erro ao ler OUs: ${error.message}`);
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Salva OUs
|
|
68
|
+
*/
|
|
69
|
+
saveOrganizationalUnits(ous) {
|
|
70
|
+
try {
|
|
71
|
+
const ouFile = path.join(this.organizationsDir, '__ous__.json');
|
|
72
|
+
const data = {};
|
|
73
|
+
|
|
74
|
+
for (const [ouId, ou] of ous) {
|
|
75
|
+
data[ouId] = ou;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fs.writeFileSync(ouFile, JSON.stringify(data, null, 2), 'utf8');
|
|
79
|
+
logger.debug(`${Object.keys(data).length} OUs salvas`);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.error(`Erro ao salvar OUs: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Carrega contas
|
|
87
|
+
*/
|
|
88
|
+
loadAccounts() {
|
|
89
|
+
const accountsFile = path.join(this.organizationsDir, '__accounts__.json');
|
|
90
|
+
|
|
91
|
+
if (fs.existsSync(accountsFile)) {
|
|
92
|
+
try {
|
|
93
|
+
const data = fs.readFileSync(accountsFile, 'utf8');
|
|
94
|
+
return JSON.parse(data);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger.warn(`Erro ao ler contas: ${error.message}`);
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Salva contas
|
|
105
|
+
*/
|
|
106
|
+
saveAccounts(accounts) {
|
|
107
|
+
try {
|
|
108
|
+
const accountsFile = path.join(this.organizationsDir, '__accounts__.json');
|
|
109
|
+
const data = {};
|
|
110
|
+
|
|
111
|
+
for (const [accountId, account] of accounts) {
|
|
112
|
+
data[accountId] = account;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fs.writeFileSync(accountsFile, JSON.stringify(data, null, 2), 'utf8');
|
|
116
|
+
logger.debug(`${Object.keys(data).length} contas salvas`);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.error(`Erro ao salvar contas: ${error.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Carrega policies
|
|
124
|
+
*/
|
|
125
|
+
loadPolicies() {
|
|
126
|
+
const policiesFile = path.join(this.organizationsDir, '__policies__.json');
|
|
127
|
+
|
|
128
|
+
if (fs.existsSync(policiesFile)) {
|
|
129
|
+
try {
|
|
130
|
+
const data = fs.readFileSync(policiesFile, 'utf8');
|
|
131
|
+
return JSON.parse(data);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
logger.warn(`Erro ao ler policies: ${error.message}`);
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Salva policies
|
|
142
|
+
*/
|
|
143
|
+
savePolicies(policies) {
|
|
144
|
+
try {
|
|
145
|
+
const policiesFile = path.join(this.organizationsDir, '__policies__.json');
|
|
146
|
+
const data = {};
|
|
147
|
+
|
|
148
|
+
for (const [policyId, policy] of policies) {
|
|
149
|
+
data[policyId] = {
|
|
150
|
+
id: policy.id,
|
|
151
|
+
name: policy.name,
|
|
152
|
+
document: policy.document,
|
|
153
|
+
type: policy.type,
|
|
154
|
+
status: policy.status,
|
|
155
|
+
targets: policy.targets,
|
|
156
|
+
createdAt: policy.createdAt,
|
|
157
|
+
lastModifiedAt: policy.lastModifiedAt
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fs.writeFileSync(policiesFile, JSON.stringify(data, null, 2), 'utf8');
|
|
162
|
+
logger.debug(`${Object.keys(data).length} policies salvas`);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
logger.error(`Erro ao salvar policies: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Carrega roles
|
|
170
|
+
*/
|
|
171
|
+
loadRoles() {
|
|
172
|
+
const rolesFile = path.join(this.organizationsDir, '__roles__.json');
|
|
173
|
+
|
|
174
|
+
if (fs.existsSync(rolesFile)) {
|
|
175
|
+
try {
|
|
176
|
+
const data = fs.readFileSync(rolesFile, 'utf8');
|
|
177
|
+
return JSON.parse(data);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
logger.warn(`Erro ao ler roles: ${error.message}`);
|
|
180
|
+
return {};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Salva roles
|
|
188
|
+
*/
|
|
189
|
+
saveRoles(roles) {
|
|
190
|
+
try {
|
|
191
|
+
const rolesFile = path.join(this.organizationsDir, '__roles__.json');
|
|
192
|
+
const data = {};
|
|
193
|
+
|
|
194
|
+
for (const [roleId, role] of roles) {
|
|
195
|
+
data[roleId] = {
|
|
196
|
+
id: role.id,
|
|
197
|
+
name: role.name,
|
|
198
|
+
arn: role.arn,
|
|
199
|
+
accountId: role.accountId,
|
|
200
|
+
trustPolicy: role.trustPolicy,
|
|
201
|
+
inlinePolicies: Array.from(role.inlinePolicies.entries()),
|
|
202
|
+
managedPolicies: role.managedPolicies,
|
|
203
|
+
createdAt: role.createdAt,
|
|
204
|
+
lastModifiedAt: role.lastModifiedAt
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fs.writeFileSync(rolesFile, JSON.stringify(data, null, 2), 'utf8');
|
|
209
|
+
logger.debug(`${Object.keys(data).length} roles salvas`);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
logger.error(`Erro ao salvar roles: ${error.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Limpa tudo
|
|
217
|
+
*/
|
|
218
|
+
clear() {
|
|
219
|
+
try {
|
|
220
|
+
if (fs.existsSync(this.organizationsDir)) {
|
|
221
|
+
const files = fs.readdirSync(this.organizationsDir);
|
|
222
|
+
files.forEach(file => {
|
|
223
|
+
if (file.endsWith('.json')) {
|
|
224
|
+
fs.unlinkSync(path.join(this.organizationsDir, file));
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
logger.debug('Todos os dados de organizações foram limpos');
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger.error(`Erro ao limpar organizações: ${error.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = OrganizationsStore;
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VPC Integration Module
|
|
3
|
+
* Integra Organizations com VPC para restrições regionais e de serviços
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const logger = require('../../utils/logger');
|
|
7
|
+
|
|
8
|
+
class VPCIntegration {
|
|
9
|
+
constructor(organizationsSimulator, vpcService, ec2Service) {
|
|
10
|
+
this.organizationsSimulator = organizationsSimulator;
|
|
11
|
+
this.vpcService = vpcService;
|
|
12
|
+
this.ec2Service = ec2Service;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Valida se um recurso VPC pode ser criado em uma conta/região
|
|
17
|
+
*/
|
|
18
|
+
validateVPCResource(accountId, region, resourceType, config = {}) {
|
|
19
|
+
logger.debug(`Validating VPC resource: ${resourceType} in ${region} for account ${accountId}`);
|
|
20
|
+
|
|
21
|
+
// 1. Verifica se SCP permite operação em serviço VPC
|
|
22
|
+
const scpEvaluation = this.organizationsSimulator.evaluateSCPs(
|
|
23
|
+
accountId,
|
|
24
|
+
`ec2:Create*`,
|
|
25
|
+
`arn:aws:ec2:${region}:${accountId}:*`
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (!scpEvaluation.allowed) {
|
|
29
|
+
return {
|
|
30
|
+
allowed: false,
|
|
31
|
+
deniedBy: 'SCP',
|
|
32
|
+
reason: `Service Control Policy denies VPC operations in region ${region}`,
|
|
33
|
+
details: scpEvaluation
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Verifica restrições regionais por SCP
|
|
38
|
+
const regionalCheck = this.checkRegionalRestrictions(accountId, region);
|
|
39
|
+
if (!regionalCheck.allowed) {
|
|
40
|
+
return {
|
|
41
|
+
allowed: false,
|
|
42
|
+
deniedBy: 'RegionalSCP',
|
|
43
|
+
reason: regionalCheck.reason,
|
|
44
|
+
details: regionalCheck
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 3. Valida tipo de recurso específico
|
|
49
|
+
const typeValidation = this.validateResourceType(resourceType, region, config);
|
|
50
|
+
if (!typeValidation.allowed) {
|
|
51
|
+
return typeValidation;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
allowed: true,
|
|
56
|
+
accountId,
|
|
57
|
+
region,
|
|
58
|
+
resourceType,
|
|
59
|
+
message: 'VPC resource creation allowed'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Verifica restrições regionais definidas em SCP
|
|
65
|
+
*/
|
|
66
|
+
checkRegionalRestrictions(accountId, region) {
|
|
67
|
+
try {
|
|
68
|
+
const account = this.organizationsSimulator.organization.getAccount(accountId);
|
|
69
|
+
if (!account) {
|
|
70
|
+
return {
|
|
71
|
+
allowed: false,
|
|
72
|
+
reason: `Account ${accountId} not found`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Coleta todas as SCPs aplicáveis à conta
|
|
77
|
+
const applicablePolicies = [];
|
|
78
|
+
|
|
79
|
+
let currentOUId = account.parentId;
|
|
80
|
+
while (currentOUId) {
|
|
81
|
+
const policies = this.organizationsSimulator.organization.listPoliciesForTarget(currentOUId);
|
|
82
|
+
applicablePolicies.push(...policies);
|
|
83
|
+
|
|
84
|
+
const ou = this.organizationsSimulator.organization.getOrganizationalUnit(currentOUId);
|
|
85
|
+
currentOUId = ou?.parentId;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const accountPolicies = this.organizationsSimulator.organization.listPoliciesForTarget(accountId);
|
|
89
|
+
applicablePolicies.push(...accountPolicies);
|
|
90
|
+
|
|
91
|
+
// Verifica se há restrições regionais
|
|
92
|
+
const allowedRegions = this.extractAllowedRegions(applicablePolicies);
|
|
93
|
+
|
|
94
|
+
if (allowedRegions.length > 0 && !allowedRegions.includes(region)) {
|
|
95
|
+
return {
|
|
96
|
+
allowed: false,
|
|
97
|
+
reason: `Region ${region} not in allowed regions: ${allowedRegions.join(', ')}`,
|
|
98
|
+
allowedRegions
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const deniedRegions = this.extractDeniedRegions(applicablePolicies);
|
|
103
|
+
if (deniedRegions.includes(region)) {
|
|
104
|
+
return {
|
|
105
|
+
allowed: false,
|
|
106
|
+
reason: `Region ${region} is explicitly denied`,
|
|
107
|
+
deniedRegions
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
allowed: true,
|
|
113
|
+
allowedRegions: allowedRegions.length > 0 ? allowedRegions : 'all',
|
|
114
|
+
deniedRegions
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.error(`Error checking regional restrictions: ${error.message}`);
|
|
119
|
+
return {
|
|
120
|
+
allowed: false,
|
|
121
|
+
reason: error.message
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extrai regiões permitidas de policies
|
|
128
|
+
*/
|
|
129
|
+
extractAllowedRegions(policies) {
|
|
130
|
+
const regions = new Set();
|
|
131
|
+
|
|
132
|
+
for (const policy of policies) {
|
|
133
|
+
if (!policy.document || !policy.document.Statement) continue;
|
|
134
|
+
|
|
135
|
+
for (const statement of policy.document.Statement) {
|
|
136
|
+
if (statement.Effect === 'Allow') {
|
|
137
|
+
// Procura por restrições de região na Resource ou Condition
|
|
138
|
+
if (statement.Resource) {
|
|
139
|
+
const resourceRegions = this.extractRegionsFromResources(statement.Resource);
|
|
140
|
+
resourceRegions.forEach(r => regions.add(r));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (statement.Condition?.StringEquals?.['aws:RequestedRegion']) {
|
|
144
|
+
const regionValues = statement.Condition.StringEquals['aws:RequestedRegion'];
|
|
145
|
+
const regionList = Array.isArray(regionValues) ? regionValues : [regionValues];
|
|
146
|
+
regionList.forEach(r => regions.add(r));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return Array.from(regions);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extrai regiões negadas de policies
|
|
157
|
+
*/
|
|
158
|
+
extractDeniedRegions(policies) {
|
|
159
|
+
const regions = new Set();
|
|
160
|
+
|
|
161
|
+
for (const policy of policies) {
|
|
162
|
+
if (!policy.document || !policy.document.Statement) continue;
|
|
163
|
+
|
|
164
|
+
for (const statement of policy.document.Statement) {
|
|
165
|
+
if (statement.Effect === 'Deny') {
|
|
166
|
+
if (statement.Resource) {
|
|
167
|
+
const resourceRegions = this.extractRegionsFromResources(statement.Resource);
|
|
168
|
+
resourceRegions.forEach(r => regions.add(r));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (statement.Condition?.StringEquals?.['aws:RequestedRegion']) {
|
|
172
|
+
const regionValues = statement.Condition.StringEquals['aws:RequestedRegion'];
|
|
173
|
+
const regionList = Array.isArray(regionValues) ? regionValues : [regionValues];
|
|
174
|
+
regionList.forEach(r => regions.add(r));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return Array.from(regions);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Extrai regiões de um ARN
|
|
185
|
+
*/
|
|
186
|
+
extractRegionsFromResources(resources) {
|
|
187
|
+
const regions = new Set();
|
|
188
|
+
const resourceList = Array.isArray(resources) ? resources : [resources];
|
|
189
|
+
|
|
190
|
+
for (const resource of resourceList) {
|
|
191
|
+
// ARN format: arn:aws:service:region:account:resource
|
|
192
|
+
const parts = resource.split(':');
|
|
193
|
+
if (parts.length >= 4) {
|
|
194
|
+
const region = parts[3];
|
|
195
|
+
if (region && region !== '*') {
|
|
196
|
+
regions.add(region);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return Array.from(regions);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Valida tipo de recurso específico
|
|
206
|
+
*/
|
|
207
|
+
validateResourceType(resourceType, region, config = {}) {
|
|
208
|
+
try {
|
|
209
|
+
switch (resourceType.toLowerCase()) {
|
|
210
|
+
case 'vpc':
|
|
211
|
+
return this.validateVPC(region, config);
|
|
212
|
+
|
|
213
|
+
case 'subnet':
|
|
214
|
+
return this.validateSubnet(region, config);
|
|
215
|
+
|
|
216
|
+
case 'securitygroup':
|
|
217
|
+
case 'security-group':
|
|
218
|
+
return this.validateSecurityGroup(region, config);
|
|
219
|
+
|
|
220
|
+
case 'routetable':
|
|
221
|
+
case 'route-table':
|
|
222
|
+
return this.validateRouteTable(region, config);
|
|
223
|
+
|
|
224
|
+
case 'internetgateway':
|
|
225
|
+
case 'internet-gateway':
|
|
226
|
+
return this.validateInternetGateway(region, config);
|
|
227
|
+
|
|
228
|
+
case 'natgateway':
|
|
229
|
+
case 'nat-gateway':
|
|
230
|
+
return this.validateNATGateway(region, config);
|
|
231
|
+
|
|
232
|
+
default:
|
|
233
|
+
return {
|
|
234
|
+
allowed: true,
|
|
235
|
+
message: `Resource type ${resourceType} validation passed`
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger.error(`Error validating resource type ${resourceType}: ${error.message}`);
|
|
240
|
+
return {
|
|
241
|
+
allowed: false,
|
|
242
|
+
reason: error.message
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Valida criação de VPC
|
|
249
|
+
*/
|
|
250
|
+
validateVPC(region, config = {}) {
|
|
251
|
+
// Validações específicas para VPC
|
|
252
|
+
if (!config.cidrBlock) {
|
|
253
|
+
return {
|
|
254
|
+
allowed: false,
|
|
255
|
+
reason: 'CIDR block is required for VPC'
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Verifica se CIDR é válido
|
|
260
|
+
if (!this.isValidCIDR(config.cidrBlock)) {
|
|
261
|
+
return {
|
|
262
|
+
allowed: false,
|
|
263
|
+
reason: `Invalid CIDR block: ${config.cidrBlock}`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
allowed: true,
|
|
269
|
+
message: 'VPC creation validation passed'
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Valida criação de Subnet
|
|
275
|
+
*/
|
|
276
|
+
validateSubnet(region, config = {}) {
|
|
277
|
+
if (!config.vpcId) {
|
|
278
|
+
return {
|
|
279
|
+
allowed: false,
|
|
280
|
+
reason: 'VPC ID is required for subnet'
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!config.cidrBlock) {
|
|
285
|
+
return {
|
|
286
|
+
allowed: false,
|
|
287
|
+
reason: 'CIDR block is required for subnet'
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!this.isValidCIDR(config.cidrBlock)) {
|
|
292
|
+
return {
|
|
293
|
+
allowed: false,
|
|
294
|
+
reason: `Invalid CIDR block: ${config.cidrBlock}`
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
allowed: true,
|
|
300
|
+
message: 'Subnet creation validation passed'
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Valida criação de Security Group
|
|
306
|
+
*/
|
|
307
|
+
validateSecurityGroup(region, config = {}) {
|
|
308
|
+
if (!config.groupName) {
|
|
309
|
+
return {
|
|
310
|
+
allowed: false,
|
|
311
|
+
reason: 'Group name is required'
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
allowed: true,
|
|
317
|
+
message: 'Security group creation validation passed'
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Valida criação de Route Table
|
|
323
|
+
*/
|
|
324
|
+
validateRouteTable(region, config = {}) {
|
|
325
|
+
if (!config.vpcId) {
|
|
326
|
+
return {
|
|
327
|
+
allowed: false,
|
|
328
|
+
reason: 'VPC ID is required for route table'
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
allowed: true,
|
|
334
|
+
message: 'Route table creation validation passed'
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Valida criação de Internet Gateway
|
|
340
|
+
*/
|
|
341
|
+
validateInternetGateway(region, config = {}) {
|
|
342
|
+
return {
|
|
343
|
+
allowed: true,
|
|
344
|
+
message: 'Internet gateway creation validation passed'
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Valida criação de NAT Gateway
|
|
350
|
+
*/
|
|
351
|
+
validateNATGateway(region, config = {}) {
|
|
352
|
+
if (!config.subnetId) {
|
|
353
|
+
return {
|
|
354
|
+
allowed: false,
|
|
355
|
+
reason: 'Subnet ID is required for NAT gateway'
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!config.allocationId) {
|
|
360
|
+
return {
|
|
361
|
+
allowed: false,
|
|
362
|
+
reason: 'Elastic IP allocation ID is required for NAT gateway'
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
allowed: true,
|
|
368
|
+
message: 'NAT gateway creation validation passed'
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Valida CIDR block
|
|
374
|
+
*/
|
|
375
|
+
isValidCIDR(cidr) {
|
|
376
|
+
const cidrRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/;
|
|
377
|
+
|
|
378
|
+
if (!cidrRegex.test(cidr)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Valida octetos
|
|
383
|
+
const [network, prefix] = cidr.split('/');
|
|
384
|
+
const octets = network.split('.');
|
|
385
|
+
|
|
386
|
+
for (const octet of octets) {
|
|
387
|
+
const num = parseInt(octet, 10);
|
|
388
|
+
if (num < 0 || num > 255) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Valida prefix length
|
|
394
|
+
const prefixLen = parseInt(prefix, 10);
|
|
395
|
+
return prefixLen >= 0 && prefixLen <= 32;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Valida regra de Security Group
|
|
400
|
+
*/
|
|
401
|
+
validateSecurityGroupRule(rule, direction = 'ingress') {
|
|
402
|
+
if (!rule.protocol) {
|
|
403
|
+
return {
|
|
404
|
+
allowed: false,
|
|
405
|
+
reason: 'Protocol is required'
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (rule.protocol !== '-1' && !['tcp', 'udp', 'icmp'].includes(rule.protocol.toLowerCase())) {
|
|
410
|
+
return {
|
|
411
|
+
allowed: false,
|
|
412
|
+
reason: `Invalid protocol: ${rule.protocol}`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (rule.protocol !== '-1' && direction === 'ingress') {
|
|
417
|
+
if (rule.fromPort === undefined || rule.toPort === undefined) {
|
|
418
|
+
return {
|
|
419
|
+
allowed: false,
|
|
420
|
+
reason: 'Port range is required for protocol'
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (rule.fromPort < 0 || rule.fromPort > 65535) {
|
|
425
|
+
return {
|
|
426
|
+
allowed: false,
|
|
427
|
+
reason: `Invalid from port: ${rule.fromPort}`
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (rule.toPort < 0 || rule.toPort > 65535) {
|
|
432
|
+
return {
|
|
433
|
+
allowed: false,
|
|
434
|
+
reason: `Invalid to port: ${rule.toPort}`
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (rule.fromPort > rule.toPort) {
|
|
439
|
+
return {
|
|
440
|
+
allowed: false,
|
|
441
|
+
reason: 'From port cannot be greater than to port'
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
allowed: true,
|
|
448
|
+
message: 'Security group rule validation passed'
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
module.exports = VPCIntegration;
|