@fishawack/lab-env 5.6.1 → 5.7.0-beta.2

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.
@@ -178,6 +178,8 @@ module.exports.templates = [
178
178
  },
179
179
  ];
180
180
 
181
+ module.exports.secretGroups = ["app", "storage", "database", "opensearch"];
182
+
181
183
  module.exports.frameworks = [
182
184
  {
183
185
  name: "wordpress",
@@ -207,55 +209,7 @@ module.exports.eb = {
207
209
  python: {
208
210
  shared: [],
209
211
  low: [],
210
- high: [
211
- {
212
- OptionName: "ELBScheme",
213
- Value: "internal",
214
- Namespace: "aws:ec2:vpc",
215
- },
216
- () => {
217
- const regions = {
218
- "us-east-1": {
219
- VPCId: "vpc-d30bcca8",
220
- Subnets:
221
- "subnet-00bf5eda896fdd5c5,subnet-0aa7e40d4701683ae",
222
- },
223
- "eu-west-1": {
224
- VPCId: "vpc-cc0d9aa9",
225
- Subnets:
226
- "subnet-0e357f06e047e1c10,subnet-0956ffb043535455d",
227
- },
228
- };
229
-
230
- return [
231
- {
232
- OptionName: "VPCId",
233
- Value: regions[process.env.AWS_REGION].VPCId,
234
- Namespace: "aws:ec2:vpc",
235
- },
236
- {
237
- OptionName: "Subnets",
238
- Value: regions[process.env.AWS_REGION].Subnets,
239
- Namespace: "aws:ec2:vpc",
240
- },
241
- {
242
- OptionName: "ELBSubnets",
243
- Value: regions[process.env.AWS_REGION].Subnets,
244
- Namespace: "aws:ec2:vpc",
245
- },
246
- ];
247
- },
248
- {
249
- OptionName: "AssociatePublicIpAddress",
250
- Value: "false",
251
- Namespace: "aws:ec2:vpc",
252
- },
253
- {
254
- OptionName: "APP_ENV",
255
- Value: "production",
256
- Namespace: "aws:elasticbeanstalk:application:environment",
257
- },
258
- ],
212
+ high: [],
259
213
  },
260
214
  php: {
261
215
  shared: [],
@@ -284,233 +238,17 @@ module.exports.eb = {
284
238
  high: [],
285
239
  },
286
240
  wordpress: {
287
- shared: [
288
- {
289
- OptionName: "AUTH_KEY",
290
- Value: generateSafeKey({
291
- length: 64,
292
- numbers: true,
293
- symbols: true,
294
- }),
295
- Namespace: "aws:elasticbeanstalk:application:environment",
296
- },
297
- {
298
- OptionName: "SECURE_AUTH_KEY",
299
- Value: generateSafeKey({
300
- length: 64,
301
- numbers: true,
302
- symbols: true,
303
- }),
304
- Namespace: "aws:elasticbeanstalk:application:environment",
305
- },
306
- {
307
- OptionName: "LOGGED_IN_KEY",
308
- Value: generateSafeKey({
309
- length: 64,
310
- numbers: true,
311
- symbols: true,
312
- }),
313
- Namespace: "aws:elasticbeanstalk:application:environment",
314
- },
315
- {
316
- OptionName: "NONCE_KEY",
317
- Value: generateSafeKey({
318
- length: 64,
319
- numbers: true,
320
- symbols: true,
321
- }),
322
- Namespace: "aws:elasticbeanstalk:application:environment",
323
- },
324
- {
325
- OptionName: "AUTH_SALT",
326
- Value: generateSafeKey({
327
- length: 64,
328
- numbers: true,
329
- symbols: true,
330
- }),
331
- Namespace: "aws:elasticbeanstalk:application:environment",
332
- },
333
- {
334
- OptionName: "SECURE_AUTH_SALT",
335
- Value: generateSafeKey({
336
- length: 64,
337
- numbers: true,
338
- symbols: true,
339
- }),
340
- Namespace: "aws:elasticbeanstalk:application:environment",
341
- },
342
- {
343
- OptionName: "LOGGED_IN_SALT",
344
- Value: generateSafeKey({
345
- length: 64,
346
- numbers: true,
347
- symbols: true,
348
- }),
349
- Namespace: "aws:elasticbeanstalk:application:environment",
350
- },
351
- {
352
- OptionName: "NONCE_SALT",
353
- Value: generateSafeKey({
354
- length: 64,
355
- numbers: true,
356
- symbols: true,
357
- }),
358
- Namespace: "aws:elasticbeanstalk:application:environment",
359
- },
360
- {
361
- OptionName: "S3_UPLOADS_BUCKET",
362
- Value: "<%= s3Slug %>",
363
- Namespace: "aws:elasticbeanstalk:application:environment",
364
- },
365
- {
366
- OptionName: "S3_UPLOADS_REGION",
367
- Value: "<%= AWS_REGION %>",
368
- Namespace: "aws:elasticbeanstalk:application:environment",
369
- },
370
- {
371
- OptionName: "S3_UPLOADS_KEY",
372
- Value: "<%= AccessKeyId %>",
373
- Namespace: "aws:elasticbeanstalk:application:environment",
374
- },
375
- {
376
- OptionName: "S3_UPLOADS_SECRET",
377
- Value: "<%= SecretAccessKey %>",
378
- Namespace: "aws:elasticbeanstalk:application:environment",
379
- },
380
- ],
241
+ shared: [],
381
242
  low: [],
382
243
  high: [],
383
244
  },
384
245
  laravel: {
385
- shared: [
386
- {
387
- OptionName: "APP_ENV",
388
- Value: "production",
389
- Namespace: "aws:elasticbeanstalk:application:environment",
390
- },
391
- {
392
- OptionName: "APP_KEY",
393
- Value: `base64:${Buffer.from(generator.generate({ length: 32, numbers: true, symbols: true })).toString("base64")}`,
394
- Namespace: "aws:elasticbeanstalk:application:environment",
395
- },
396
- {
397
- OptionName: "APP_DEBUG",
398
- Value: "false",
399
- Namespace: "aws:elasticbeanstalk:application:environment",
400
- },
401
- {
402
- OptionName: "APP_URL",
403
- Value: "https://<%= DOMAIN_LINK %>",
404
- Namespace: "aws:elasticbeanstalk:application:environment",
405
- },
406
- {
407
- OptionName: "AWS_BUCKET",
408
- Value: "<%= s3Slug %>",
409
- Namespace: "aws:elasticbeanstalk:application:environment",
410
- },
411
- {
412
- OptionName: "AWS_DEFAULT_REGION",
413
- Value: "<%= AWS_REGION %>",
414
- Namespace: "aws:elasticbeanstalk:application:environment",
415
- },
416
- {
417
- OptionName: "AWS_ACCESS_KEY_ID",
418
- Value: "<%= AccessKeyId %>",
419
- Namespace: "aws:elasticbeanstalk:application:environment",
420
- },
421
- {
422
- OptionName: "AWS_SECRET_ACCESS_KEY",
423
- Value: "<%= SecretAccessKey %>",
424
- Namespace: "aws:elasticbeanstalk:application:environment",
425
- },
426
- {
427
- OptionName: "FILESYSTEM_DISK",
428
- Value: "s3",
429
- Namespace: "aws:elasticbeanstalk:application:environment",
430
- },
431
- {
432
- OptionName: "FILESYSTEM_DISK_PUBLIC",
433
- Value: "s3-public",
434
- Namespace: "aws:elasticbeanstalk:application:environment",
435
- },
436
- ],
246
+ shared: [],
437
247
  low: [],
438
- high: [
439
- {
440
- OptionName: "SESSION_DRIVER",
441
- Value: "cookie",
442
- Namespace: "aws:elasticbeanstalk:application:environment",
443
- },
444
- {
445
- OptionName: "SESSION_SECURE_COOKIE",
446
- Value: "true",
447
- Namespace: "aws:elasticbeanstalk:application:environment",
448
- },
449
- ],
248
+ high: [],
450
249
  },
451
250
  adonis: {
452
- shared: [
453
- {
454
- OptionName: "NODE_ENV",
455
- Value: "production",
456
- Namespace: "aws:elasticbeanstalk:application:environment",
457
- },
458
- {
459
- OptionName: "APP_KEY",
460
- Value: `base64:${Buffer.from(generator.generate({ length: 32, numbers: true, symbols: true })).toString("base64")}`,
461
- Namespace: "aws:elasticbeanstalk:application:environment",
462
- },
463
- {
464
- OptionName: "APP_URL",
465
- Value: "https://<%= DOMAIN_LINK %>",
466
- Namespace: "aws:elasticbeanstalk:application:environment",
467
- },
468
- {
469
- OptionName: "SESSION_DRIVER",
470
- Value: "cookie",
471
- Namespace: "aws:elasticbeanstalk:application:environment",
472
- },
473
- {
474
- OptionName: "AWS_BUCKET",
475
- Value: "<%= s3Slug %>",
476
- Namespace: "aws:elasticbeanstalk:application:environment",
477
- },
478
- {
479
- OptionName: "AWS_DEFAULT_REGION",
480
- Value: "<%= AWS_REGION %>",
481
- Namespace: "aws:elasticbeanstalk:application:environment",
482
- },
483
- {
484
- OptionName: "AWS_ACCESS_KEY_ID",
485
- Value: "<%= AccessKeyId %>",
486
- Namespace: "aws:elasticbeanstalk:application:environment",
487
- },
488
- {
489
- OptionName: "AWS_SECRET_ACCESS_KEY",
490
- Value: "<%= SecretAccessKey %>",
491
- Namespace: "aws:elasticbeanstalk:application:environment",
492
- },
493
- {
494
- OptionName: "FILESYSTEM_DISK",
495
- Value: "s3",
496
- Namespace: "aws:elasticbeanstalk:application:environment",
497
- },
498
- {
499
- OptionName: "FILESYSTEM_DISK_PUBLIC",
500
- Value: "s3-public",
501
- Namespace: "aws:elasticbeanstalk:application:environment",
502
- },
503
- {
504
- OptionName: "PORT",
505
- Value: "3333",
506
- Namespace: "aws:elasticbeanstalk:application:environment",
507
- },
508
- {
509
- OptionName: "LOG_LEVEL",
510
- Value: "info",
511
- Namespace: "aws:elasticbeanstalk:application:environment",
512
- },
513
- ],
251
+ shared: [],
514
252
  low: [],
515
253
  high: [],
516
254
  },
@@ -525,32 +263,11 @@ module.exports.eb = {
525
263
  Value: "lab-env-aws-elasticbeanstalk-service-role",
526
264
  Namespace: "aws:elasticbeanstalk:environment",
527
265
  },
528
- {
266
+ ({ instanceProfileName }) => ({
529
267
  OptionName: "IamInstanceProfile",
530
- Value: "aws-elasticbeanstalk-ec2-role",
268
+ Value: instanceProfileName || "aws-elasticbeanstalk-ec2-role",
531
269
  Namespace: "aws:autoscaling:launchconfiguration",
532
- },
533
- ({ database }) =>
534
- database && [
535
- {
536
- OptionName: "HasCoupledDatabase",
537
- Value: "true",
538
- Namespace: "aws:rds:dbinstance",
539
- },
540
- {
541
- OptionName: "DBEngineVersion",
542
- Value: "8.0.43",
543
- Namespace: "aws:rds:dbinstance",
544
- },
545
- {
546
- OptionName: "DBPassword",
547
- Value: generator.generate({
548
- length: 10,
549
- numbers: true,
550
- }),
551
- Namespace: "aws:rds:dbinstance",
552
- },
553
- ],
270
+ }),
554
271
  ],
555
272
  low: [
556
273
  {
@@ -558,16 +275,6 @@ module.exports.eb = {
558
275
  Value: "SingleInstance",
559
276
  Namespace: "aws:elasticbeanstalk:environment",
560
277
  },
561
- {
562
- OptionName: "DOMAIN_LINK",
563
- Value: "<%= DOMAIN_LINK %>",
564
- Namespace: "aws:elasticbeanstalk:application:environment",
565
- },
566
- {
567
- OptionName: "EMAIL_LINK",
568
- Value: "mike.mellor@fishawack.com",
569
- Namespace: "aws:elasticbeanstalk:application:environment",
570
- },
571
278
  ],
572
279
  high: [
573
280
  {
@@ -610,8 +317,215 @@ module.exports.eb = {
610
317
  Value: "application",
611
318
  Namespace: "aws:elasticbeanstalk:environment",
612
319
  },
320
+ ({ vpcId, privateSubnetIds, publicSubnetIds, elbScheme }) => {
321
+ const settings = [
322
+ {
323
+ OptionName: "VPCId",
324
+ Value: vpcId,
325
+ Namespace: "aws:ec2:vpc",
326
+ },
327
+ {
328
+ OptionName: "Subnets",
329
+ Value: privateSubnetIds,
330
+ Namespace: "aws:ec2:vpc",
331
+ },
332
+ {
333
+ OptionName: "ELBSubnets",
334
+ Value:
335
+ elbScheme === "internal"
336
+ ? privateSubnetIds
337
+ : publicSubnetIds,
338
+ Namespace: "aws:ec2:vpc",
339
+ },
340
+ {
341
+ OptionName: "AssociatePublicIpAddress",
342
+ Value: "false",
343
+ Namespace: "aws:ec2:vpc",
344
+ },
345
+ ];
346
+
347
+ if (elbScheme === "internal") {
348
+ settings.push({
349
+ OptionName: "ELBScheme",
350
+ Value: "internal",
351
+ Namespace: "aws:ec2:vpc",
352
+ });
353
+ }
354
+
355
+ return settings;
356
+ },
613
357
  ],
614
358
  },
359
+ secretPayloads: {
360
+ shared: {
361
+ database: ({ database }) =>
362
+ database && {
363
+ RDS_HOSTNAME: "<%= RDS_HOSTNAME %>",
364
+ RDS_PORT: "<%= RDS_PORT %>",
365
+ RDS_DB_NAME: "ebdb",
366
+ RDS_USERNAME: "ebroot",
367
+ RDS_PASSWORD: "<%= RDS_PASSWORD %>",
368
+ MYSQL_ATTR_SSL_CA: "/opt/rds-certs/global-bundle.pem",
369
+ },
370
+ },
371
+ low: {
372
+ app: {
373
+ DOMAIN_LINK: "<%= DOMAIN_LINK %>",
374
+ EMAIL_LINK: "mike.mellor@fishawack.com",
375
+ },
376
+ },
377
+ high: {},
378
+ wordpress: {
379
+ shared: {
380
+ app: {
381
+ AUTH_KEY: generateSafeKey({
382
+ length: 64,
383
+ numbers: true,
384
+ symbols: true,
385
+ }),
386
+ SECURE_AUTH_KEY: generateSafeKey({
387
+ length: 64,
388
+ numbers: true,
389
+ symbols: true,
390
+ }),
391
+ LOGGED_IN_KEY: generateSafeKey({
392
+ length: 64,
393
+ numbers: true,
394
+ symbols: true,
395
+ }),
396
+ NONCE_KEY: generateSafeKey({
397
+ length: 64,
398
+ numbers: true,
399
+ symbols: true,
400
+ }),
401
+ AUTH_SALT: generateSafeKey({
402
+ length: 64,
403
+ numbers: true,
404
+ symbols: true,
405
+ }),
406
+ SECURE_AUTH_SALT: generateSafeKey({
407
+ length: 64,
408
+ numbers: true,
409
+ symbols: true,
410
+ }),
411
+ LOGGED_IN_SALT: generateSafeKey({
412
+ length: 64,
413
+ numbers: true,
414
+ symbols: true,
415
+ }),
416
+ NONCE_SALT: generateSafeKey({
417
+ length: 64,
418
+ numbers: true,
419
+ symbols: true,
420
+ }),
421
+ },
422
+ storage: ({ storage }) =>
423
+ storage && {
424
+ S3_UPLOADS_BUCKET: "<%= s3Slug %>",
425
+ S3_UPLOADS_REGION: "<%= AWS_REGION %>",
426
+ S3_UPLOADS_KEY: "<%= AccessKeyId %>",
427
+ S3_UPLOADS_SECRET: "<%= SecretAccessKey %>",
428
+ },
429
+ },
430
+ },
431
+ laravel: {
432
+ shared: {
433
+ app: {
434
+ APP_NAME: "<%= APP_NAME %>",
435
+ APP_ENV: "production",
436
+ APP_KEY: `base64:${Buffer.from(generator.generate({ length: 32, numbers: true, symbols: true })).toString("base64")}`,
437
+ APP_DEBUG: "false",
438
+ APP_URL: "https://<%= APP_DOMAIN %>",
439
+ },
440
+ storage: ({ storage }) =>
441
+ storage && {
442
+ AWS_BUCKET: "<%= s3Slug %>",
443
+ AWS_DEFAULT_REGION: "<%= AWS_REGION %>",
444
+ AWS_ACL_VISIBILITY: "private",
445
+ FILESYSTEM_DISK: "s3",
446
+ FILESYSTEM_DISK_PUBLIC: "s3-public",
447
+ },
448
+ opensearch: ({ opensearch }) =>
449
+ opensearch && {
450
+ SEARCH_HOST: "<%= SEARCH_HOST %>",
451
+ },
452
+ },
453
+ high: {
454
+ app: {
455
+ SESSION_DRIVER: "database",
456
+ SESSION_SECURE_COOKIE: "true",
457
+ },
458
+ },
459
+ },
460
+ adonis: {
461
+ shared: {
462
+ app: {
463
+ NODE_ENV: "production",
464
+ APP_KEY: `base64:${Buffer.from(generator.generate({ length: 32, numbers: true, symbols: true })).toString("base64")}`,
465
+ APP_URL: "https://<%= DOMAIN_LINK %>",
466
+ SESSION_DRIVER: "cookie",
467
+ PORT: "3333",
468
+ LOG_LEVEL: "info",
469
+ },
470
+ storage: ({ storage }) =>
471
+ storage && {
472
+ AWS_BUCKET: "<%= s3Slug %>",
473
+ AWS_DEFAULT_REGION: "<%= AWS_REGION %>",
474
+ AWS_ACCESS_KEY_ID: "<%= AccessKeyId %>",
475
+ AWS_SECRET_ACCESS_KEY: "<%= SecretAccessKey %>",
476
+ FILESYSTEM_DISK: "s3",
477
+ FILESYSTEM_DISK_PUBLIC: "s3-public",
478
+ },
479
+ },
480
+ },
481
+ python: {
482
+ high: {
483
+ app: {
484
+ APP_ENV: "production",
485
+ },
486
+ },
487
+ },
488
+ },
489
+ buildSecretPayloads(config, data = {}) {
490
+ const { framework, availability = "low", platform, language } = config;
491
+ const result = { app: {}, storage: {}, database: {}, opensearch: {} };
492
+
493
+ const sources = [
494
+ this.secretPayloads.shared,
495
+ this.secretPayloads[availability],
496
+ this.secretPayloads[platform]?.shared,
497
+ this.secretPayloads[platform]?.[availability],
498
+ this.secretPayloads[language]?.shared,
499
+ this.secretPayloads[language]?.[availability],
500
+ this.secretPayloads[framework]?.shared,
501
+ this.secretPayloads[framework]?.[availability],
502
+ ];
503
+
504
+ for (const source of sources) {
505
+ if (!source) continue;
506
+ for (const group of module.exports.secretGroups) {
507
+ let values = source[group];
508
+ if (typeof values === "function") {
509
+ values = values(config, data);
510
+ }
511
+ if (values) {
512
+ Object.assign(result[group], values);
513
+ }
514
+ }
515
+ }
516
+
517
+ // Apply lodash template interpolation
518
+ const templateData = { ...process.env, ...data };
519
+ for (const group of module.exports.secretGroups) {
520
+ if (Object.keys(result[group]).length) {
521
+ result[group] = JSON.parse(
522
+ template(JSON.stringify(result[group]))(templateData),
523
+ );
524
+ }
525
+ }
526
+
527
+ return result;
528
+ },
615
529
  configurations: {
616
530
  php: {
617
531
  shared: [".ebextensions/php/ini.config"],
@@ -680,6 +594,7 @@ module.exports.eb = {
680
594
  ".platform/nginx/conf.d/elasticbeanstalk/laravel.conf",
681
595
  ".ebextensions/laravel/post-deploy.config",
682
596
  ".ebextensions/laravel/software.config",
597
+ ".ebextensions/laravel/rds-ssl.config",
683
598
  ],
684
599
  low: [],
685
600
  high: [],
@@ -0,0 +1,59 @@
1
+ const { ACMClient, ListCertificatesCommand } = require("@aws-sdk/client-acm");
2
+ const { Spinner } = require("../../libs/utilities");
3
+
4
+ module.exports.listCertificates = async () => {
5
+ const client = new ACMClient({});
6
+ const certs = [];
7
+ let nextToken;
8
+
9
+ do {
10
+ const res = await client.send(
11
+ new ListCertificatesCommand({
12
+ CertificateStatuses: ["ISSUED"],
13
+ NextToken: nextToken,
14
+ }),
15
+ );
16
+ certs.push(...(res.CertificateSummaryList || []));
17
+ nextToken = res.NextToken;
18
+ } while (nextToken);
19
+
20
+ return certs;
21
+ };
22
+
23
+ module.exports.findCertificatesForDomain = async (domain) => {
24
+ const certs = await Spinner.prototype.simple(
25
+ `Looking up ACM certificates for ${domain}`,
26
+ () => module.exports.listCertificates(),
27
+ );
28
+
29
+ if (!certs.length) return [];
30
+
31
+ // Extract apex domain (e.g. "test.avh-det.com" → "avh-det.com")
32
+ const parts = domain.split(".");
33
+ const apex = parts.length > 2 ? parts.slice(-2).join(".") : domain;
34
+ const wildcard = `*.${apex}`;
35
+
36
+ // Check all cert domains (DomainName + SANs)
37
+ const getCertDomains = (cert) => {
38
+ const domains = [cert.DomainName];
39
+ if (cert.SubjectAlternativeNameSummaries) {
40
+ domains.push(...cert.SubjectAlternativeNameSummaries);
41
+ }
42
+ return domains;
43
+ };
44
+
45
+ const candidates = [domain, wildcard];
46
+ if (apex !== domain) candidates.push(apex);
47
+
48
+ const matches = certs.filter((c) =>
49
+ getCertDomains(c).some((d) => candidates.includes(d)),
50
+ );
51
+
52
+ // Deduplicate by ARN
53
+ const seen = new Set();
54
+ return matches.filter((c) => {
55
+ if (seen.has(c.CertificateArn)) return false;
56
+ seen.add(c.CertificateArn);
57
+ return true;
58
+ });
59
+ };