underpost 3.2.80 → 3.3.0
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/.github/workflows/ghpkg.ci.yml +7 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +1 -9
- package/CHANGELOG.md +291 -1
- package/CLI-HELP.md +174 -23
- package/README.md +5 -2
- package/bin/build.js +7 -5
- package/bin/deploy.js +19 -17
- package/deploy/lib/logging.sh +96 -0
- package/deploy/pwa-microservices-template/deploy.sh +72 -0
- package/deploy/release/deploy.sh +62 -0
- package/docker-compose.yml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +52 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/manifests/mongodb/kustomization.yaml +4 -1
- package/manifests/mongodb/statefulset.yaml +4 -0
- package/manifests/mongodb/storage-class.yaml +9 -2
- package/package.json +19 -19
- package/scripts/audit-selinux.sh +64 -0
- package/scripts/coverall-test.sh +24 -0
- package/scripts/gpu-diag.sh +0 -0
- package/scripts/ip-info.sh +0 -0
- package/scripts/k3s-node-setup.sh +18 -15
- package/scripts/kubeadm-node-setup.sh +12 -23
- package/scripts/link-local-underpost-cli.sh +0 -0
- package/scripts/lxd-vm-setup.sh +0 -0
- package/scripts/maas-nat-firewalld.sh +0 -0
- package/scripts/nat-iptables.sh +12 -4
- package/scripts/rhel-grpc-setup.sh +0 -0
- package/scripts/rocky-kickstart.sh +25 -9
- package/scripts/test-monitor.sh +4 -3
- package/src/cli/baremetal.js +1 -2
- package/src/cli/cloud-init.js +1 -1
- package/src/cli/cluster.js +786 -96
- package/src/cli/db.js +11 -4
- package/src/cli/deploy.js +1698 -177
- package/src/cli/docker-compose.js +19 -178
- package/src/cli/env.js +1 -1
- package/src/cli/image.js +15 -7
- package/src/cli/index.js +245 -44
- package/src/cli/ipfs.js +82 -11
- package/src/cli/lxd.js +1 -1
- package/src/cli/monitor.js +2 -2
- package/src/cli/release.js +57 -22
- package/src/cli/repository.js +12 -10
- package/src/cli/run.js +2195 -427
- package/src/cli/secrets.js +969 -0
- package/src/cli/ssh.js +206 -105
- package/src/cli/system.js +26 -13
- package/src/cli/test.js +1 -1
- package/src/cli/vultr.js +583 -0
- package/src/cli/wireguard.js +2125 -0
- package/src/client-builder/client-build.js +102 -13
- package/src/client-builder/ssr.js +27 -73
- package/src/db/mongo/MongoBootstrap.js +295 -54
- package/src/db/mongo/MongooseDB.js +51 -32
- package/src/index.js +25 -1
- package/src/projects/underpost/catalog-underpost.js +4 -1
- package/src/server/backup.js +1 -1
- package/src/server/conf.js +1216 -168
- package/src/server/cri.js +70 -0
- package/src/server/cron.js +249 -51
- package/src/server/dns.js +100 -6
- package/src/server/environment.js +98 -0
- package/src/server/forward-proxy.js +549 -0
- package/src/server/middlewares.js +56 -1
- package/src/server/process.js +0 -1
- package/src/server/selinux.js +185 -0
- package/src/server/systemd.js +205 -0
- package/src/server/underpost-compression.js +186 -0
- package/src/server/underpost-gateway.js +1083 -0
- package/src/server/underpost-ingress.js +380 -0
- package/test/cluster-instances.test.js +435 -0
- package/test/deploy-node-placement.test.js +45 -0
- package/test/instance-traffic-plan.test.js +710 -0
- package/test/selinux.test.js +71 -0
- package/test/sops-secret-store.test.js +612 -0
- package/test/underpost-gateway.test.js +510 -0
- package/test/underpost-ingress.test.js +305 -0
- package/test/wireguard-edge.test.js +1177 -0
package/src/server/dns.js
CHANGED
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
* @namespace UnderpostDns
|
|
6
6
|
*/
|
|
7
7
|
import axios from 'axios';
|
|
8
|
-
import fs from 'fs';
|
|
9
8
|
import validator from 'validator';
|
|
10
9
|
import { loggerFactory } from './logger.js';
|
|
11
10
|
import dns from 'node:dns';
|
|
12
11
|
import os from 'node:os';
|
|
13
12
|
import { shellExec, pbcopy } from './process.js';
|
|
14
13
|
import Underpost from '../index.js';
|
|
15
|
-
import {
|
|
16
|
-
import { resolveDeployId } from './cron.js';
|
|
14
|
+
import { readConfJson } from './conf.js';
|
|
17
15
|
|
|
18
16
|
const logger = loggerFactory(import.meta);
|
|
19
17
|
|
|
@@ -271,6 +269,83 @@ class Dns {
|
|
|
271
269
|
logger.info('Cleared all egress bans.');
|
|
272
270
|
}
|
|
273
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Blocks all outbound traffic from this host, except for established/related connections.
|
|
274
|
+
* This is useful for security purposes, especially in a dynamic DNS context where you want to prevent
|
|
275
|
+
* any new outbound connections while still allowing existing ones (like SSH) to continue.
|
|
276
|
+
* @static
|
|
277
|
+
* @memberof UnderpostDns
|
|
278
|
+
*/
|
|
279
|
+
static blockAllEgress() {
|
|
280
|
+
// Clear any existing egress rules.
|
|
281
|
+
shellExec(`sudo nft flush chain inet filter output`, { silent: true });
|
|
282
|
+
shellExec(`sudo nft flush chain inet filter forward`, { silent: true });
|
|
283
|
+
|
|
284
|
+
// Allow return traffic for established/related connections.
|
|
285
|
+
// This keeps existing inbound connections such as SSH alive.
|
|
286
|
+
shellExec(`sudo nft add rule inet filter output ct state established,related counter accept`, { silent: true });
|
|
287
|
+
|
|
288
|
+
// Block all new outbound connections from this host and forwarded traffic.
|
|
289
|
+
shellExec(`sudo nft chain inet filter output '{ policy drop; }'`, { silent: true });
|
|
290
|
+
shellExec(`sudo nft chain inet filter forward '{ policy drop; }'`, { silent: true });
|
|
291
|
+
|
|
292
|
+
logger.info('All outbound traffic blocked.');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Unblocks all outbound traffic from this host and forwarded interfaces.
|
|
297
|
+
* Restores default output and forward chain policies to ACCEPT and clears egress rules.
|
|
298
|
+
* @static
|
|
299
|
+
* @memberof UnderpostDns
|
|
300
|
+
*/
|
|
301
|
+
static unblockAllEgress() {
|
|
302
|
+
// Restore default chain policies to accept all traffic.
|
|
303
|
+
shellExec(`sudo nft chain inet filter output '{ policy accept; }'`, { silent: true });
|
|
304
|
+
shellExec(`sudo nft chain inet filter forward '{ policy accept; }'`, { silent: true });
|
|
305
|
+
|
|
306
|
+
// Clear any existing egress blocking rules.
|
|
307
|
+
shellExec(`sudo nft flush chain inet filter output`, { silent: true });
|
|
308
|
+
shellExec(`sudo nft flush chain inet filter forward`, { silent: true });
|
|
309
|
+
|
|
310
|
+
logger.info('All outbound traffic unblocked and restored to default ACCEPT policy.');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Blocks all new inbound traffic to this host, except for established/related connections.
|
|
315
|
+
* This prevents any new incoming connections while keeping existing sessions (like SSH) alive.
|
|
316
|
+
* @static
|
|
317
|
+
* @memberof UnderpostDns
|
|
318
|
+
*/
|
|
319
|
+
static blockAllIngress() {
|
|
320
|
+
// Clear any existing ingress rules.
|
|
321
|
+
shellExec(`sudo nft flush chain inet filter input`, { silent: true });
|
|
322
|
+
|
|
323
|
+
// Allow return traffic for established/related connections.
|
|
324
|
+
// This keeps active inbound/outbound sessions alive.
|
|
325
|
+
shellExec(`sudo nft add rule inet filter input ct state established,related counter accept`, { silent: true });
|
|
326
|
+
|
|
327
|
+
// Block all new inbound connections to this host.
|
|
328
|
+
shellExec(`sudo nft chain inet filter input '{ policy drop; }'`, { silent: true });
|
|
329
|
+
|
|
330
|
+
logger.info('All new inbound traffic blocked.');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Unblocks all inbound traffic to this host.
|
|
335
|
+
* Restores default input chain policy to ACCEPT and clears ingress rules.
|
|
336
|
+
* @static
|
|
337
|
+
* @memberof UnderpostDns
|
|
338
|
+
*/
|
|
339
|
+
static unblockAllIngress() {
|
|
340
|
+
// Restore default chain policy to accept all incoming traffic.
|
|
341
|
+
shellExec(`sudo nft chain inet filter input '{ policy accept; }'`, { silent: true });
|
|
342
|
+
|
|
343
|
+
// Clear any existing ingress blocking rules.
|
|
344
|
+
shellExec(`sudo nft flush chain inet filter input`, { silent: true });
|
|
345
|
+
|
|
346
|
+
logger.info('All inbound traffic unblocked and restored to default ACCEPT policy.');
|
|
347
|
+
}
|
|
348
|
+
|
|
274
349
|
/**
|
|
275
350
|
* Performs the dynamic DNS update logic.
|
|
276
351
|
* It checks if the public IP has changed and, if so, updates the configured DNS records.
|
|
@@ -279,10 +354,8 @@ class Dns {
|
|
|
279
354
|
* @memberof UnderpostDns
|
|
280
355
|
* @param {string} deployList Comma-separated string of deployment IDs to process.
|
|
281
356
|
* @returns {Promise<void>}
|
|
282
|
-
|
|
357
|
+
*/
|
|
283
358
|
static async callback(deployList) {
|
|
284
|
-
// loadCronDeployEnv();
|
|
285
|
-
|
|
286
359
|
const isOnline = await Dns.isInternetConnection();
|
|
287
360
|
|
|
288
361
|
if (!isOnline) return;
|
|
@@ -440,6 +513,10 @@ class Dns {
|
|
|
440
513
|
* @property {boolean} [options.banEgressClear=false] - Clear all banned egress IPs.
|
|
441
514
|
* @property {boolean} [options.banBothAdd=false] - Ban IPs from both ingress and egress.
|
|
442
515
|
* @property {boolean} [options.banBothRemove=false] - Unban IPs from both ingress and egress.
|
|
516
|
+
* @property {boolean} [options.blockAllEgress=false] - Block all outbound traffic from this host.
|
|
517
|
+
* @property {boolean} [options.unblockAllEgress=false] - Unblock all outbound traffic.
|
|
518
|
+
* @property {boolean} [options.blockAllIngress=false] - Block all new inbound traffic to this host.
|
|
519
|
+
* @property {boolean} [options.unblockAllIngress=false] - Unblock all inbound traffic.
|
|
443
520
|
* @property {boolean} [options.dhcp=false] - Get local DHCP IP instead of public IP.
|
|
444
521
|
* @property {boolean} [options.copy=false] - Copy the public IP to clipboard.
|
|
445
522
|
* @return {Promise<string|void>} The public IP if no ban/unban action is taken.
|
|
@@ -457,6 +534,10 @@ class Dns {
|
|
|
457
534
|
banEgressClear: false,
|
|
458
535
|
banBothAdd: false,
|
|
459
536
|
banBothRemove: false,
|
|
537
|
+
blockAllEgress: false,
|
|
538
|
+
unblockAllEgress: false,
|
|
539
|
+
blockAllIngress: false,
|
|
540
|
+
unblockAllIngress: false,
|
|
460
541
|
copy: false,
|
|
461
542
|
dhcp: false,
|
|
462
543
|
},
|
|
@@ -507,6 +588,19 @@ class Dns {
|
|
|
507
588
|
});
|
|
508
589
|
}
|
|
509
590
|
|
|
591
|
+
if (options.blockAllEgress) {
|
|
592
|
+
return Dns.blockAllEgress();
|
|
593
|
+
}
|
|
594
|
+
if (options.unblockAllEgress) {
|
|
595
|
+
return Dns.unblockAllEgress();
|
|
596
|
+
}
|
|
597
|
+
if (options.blockAllIngress) {
|
|
598
|
+
return Dns.blockAllIngress();
|
|
599
|
+
}
|
|
600
|
+
if (options.unblockAllIngress) {
|
|
601
|
+
return Dns.unblockAllIngress();
|
|
602
|
+
}
|
|
603
|
+
|
|
510
604
|
if (options.mac) {
|
|
511
605
|
const mac = Dns.getMainInterfaceMac();
|
|
512
606
|
console.log(mac);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment and global installation path resolution.
|
|
3
|
+
* @module src/server/environment.js
|
|
4
|
+
* @namespace ServerEnvironment
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
import dotenv from 'dotenv';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { loggerFactory } from './logger.js';
|
|
12
|
+
|
|
13
|
+
const logger = loggerFactory(import.meta);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Node directory backing every hostPath PersistentVolume the deploy flow
|
|
17
|
+
* materializes (`<root>/<pv id>`), including the shared gateway's static tree.
|
|
18
|
+
* Cluster bring-up gives it the shared container label, because the pods that
|
|
19
|
+
* read these documents are unprivileged.
|
|
20
|
+
* @constant {string}
|
|
21
|
+
* @memberof ServerEnvironment
|
|
22
|
+
*/
|
|
23
|
+
const HOST_VOLUME_ROOT = '/home/dd/engine/volume';
|
|
24
|
+
const envFileCache = new Map();
|
|
25
|
+
let rootEnvPath;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the global npm module directory.
|
|
29
|
+
* @returns {string}
|
|
30
|
+
* @memberof ServerEnvironment
|
|
31
|
+
*/
|
|
32
|
+
const getNpmRootPath = () => {
|
|
33
|
+
try {
|
|
34
|
+
return execFileSync('npm', ['root', '-g'], {
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
37
|
+
}).trim();
|
|
38
|
+
} catch {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolves the global Underpost installation directory.
|
|
45
|
+
* @returns {string}
|
|
46
|
+
* @memberof ServerEnvironment
|
|
47
|
+
*/
|
|
48
|
+
const getUnderpostRootPath = () => {
|
|
49
|
+
const npmRoot = getNpmRootPath();
|
|
50
|
+
return npmRoot ? `${npmRoot}/underpost` : '';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const readEnvFile = (path) => {
|
|
54
|
+
if (envFileCache.has(path)) return envFileCache.get(path);
|
|
55
|
+
let values = {};
|
|
56
|
+
try {
|
|
57
|
+
if (path && fs.existsSync(path)) values = dotenv.parse(fs.readFileSync(path, 'utf8'));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.warn('Ignoring unreadable env file', { target: path, message: error.message });
|
|
60
|
+
}
|
|
61
|
+
envFileCache.set(path, values);
|
|
62
|
+
return values;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const environmentValueFactory = (key) => {
|
|
66
|
+
const processValue = `${process.env[key] ?? ''}`.trim();
|
|
67
|
+
if (processValue) return processValue;
|
|
68
|
+
|
|
69
|
+
if (rootEnvPath === undefined) {
|
|
70
|
+
const underpostRoot = getUnderpostRootPath();
|
|
71
|
+
rootEnvPath = underpostRoot ? `${underpostRoot}/.env` : '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const path of ['./.env', rootEnvPath]) {
|
|
75
|
+
const value = `${readEnvFile(path)[key] ?? ''}`.trim();
|
|
76
|
+
if (value) return value;
|
|
77
|
+
}
|
|
78
|
+
return '';
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Writes environment values as a dotenv file.
|
|
83
|
+
* @method writeEnv
|
|
84
|
+
* @param {string} envPath - Destination file path.
|
|
85
|
+
* @param {Object<string, *>} envObj - Environment values keyed by variable name.
|
|
86
|
+
* @returns {void}
|
|
87
|
+
* @memberof ServerEnvironment
|
|
88
|
+
*/
|
|
89
|
+
const writeEnv = (envPath, envObj) =>
|
|
90
|
+
fs.writeFileSync(
|
|
91
|
+
envPath,
|
|
92
|
+
Object.keys(envObj)
|
|
93
|
+
.map((key) => `${key}=${envObj[key]}`)
|
|
94
|
+
.join('\n'),
|
|
95
|
+
'utf8',
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
export { environmentValueFactory, getNpmRootPath, getUnderpostRootPath, HOST_VOLUME_ROOT, writeEnv };
|