@envsync-cloud/deploy-cli 0.8.15 → 0.8.16

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.
Files changed (2) hide show
  1. package/dist/index.js +118 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5181,6 +5181,30 @@ function validateStaticBundle(kind, targetDir) {
5181
5181
  }
5182
5182
 
5183
5183
  // src/index.ts
5184
+ var NETUTILS_EXPOSE_ALL_START_PORT = 15e3;
5185
+ var NETUTILS_EXPOSE_ALL_PORTS = [
5186
+ { service: "postgres", service_port: 5432, protocol: "tcp" },
5187
+ { service: "envsync_api_blue", service_port: 4e3, protocol: "tcp" },
5188
+ { service: "envsync_api_green", service_port: 4e3, protocol: "tcp" },
5189
+ { service: "envsync-management-api", service_port: 4001, protocol: "tcp" },
5190
+ { service: "keycloak", service_port: 8080, protocol: "tcp" },
5191
+ { service: "openfga", service_port: 8090, protocol: "tcp" },
5192
+ { service: "minikms", service_port: 50051, protocol: "tcp" },
5193
+ { service: "keycloak_db", service_port: 5432, protocol: "tcp" },
5194
+ { service: "openfga_db", service_port: 5432, protocol: "tcp" },
5195
+ { service: "minikms_db", service_port: 5432, protocol: "tcp" },
5196
+ { service: "redis", service_port: 6379, protocol: "tcp" },
5197
+ { service: "rustfs", service_port: 9e3, protocol: "tcp" },
5198
+ { service: "rustfs", service_port: 9001, protocol: "tcp" },
5199
+ { service: "clickstack", service_port: 8080, protocol: "tcp" },
5200
+ { service: "otel-agent", service_port: 4317, protocol: "tcp" },
5201
+ { service: "otel-agent", service_port: 4318, protocol: "tcp" },
5202
+ { service: "traefik", service_port: 80, protocol: "tcp" },
5203
+ { service: "traefik", service_port: 443, protocol: "tcp" },
5204
+ { service: "web_nginx", service_port: 80, protocol: "tcp" },
5205
+ { service: "landing_nginx", service_port: 80, protocol: "tcp" },
5206
+ { service: "api_maintenance", service_port: 80, protocol: "tcp" }
5207
+ ];
5184
5208
  var HOST_ROOT = process.env.ENVSYNC_HOST_ROOT ?? "/opt/envsync";
5185
5209
  var ETC_ROOT = process.env.ENVSYNC_ETC_ROOT ?? "/etc/envsync";
5186
5210
  var TRAEFIK_STATE_ROOT = process.env.ENVSYNC_TRAEFIK_STATE_ROOT ?? "/var/lib/envsync/traefik";
@@ -5386,6 +5410,69 @@ ${stderr}` : ""}`);
5386
5410
  }
5387
5411
  return (result.stdout ?? "").toString().trim();
5388
5412
  }
5413
+ function chunkByMax(items, max) {
5414
+ if (items.length === 0) return [];
5415
+ const chunks = [];
5416
+ for (let i = 0; i < items.length; i += max) {
5417
+ chunks.push(items.slice(i, i + max));
5418
+ }
5419
+ return chunks;
5420
+ }
5421
+ function formatPortRangesForIptables(ports) {
5422
+ const sortedPorts = Array.from(new Set(ports)).map((port) => parseInt(String(port), 10)).filter((port) => Number.isInteger(port) && port >= 1 && port <= 65535).sort((a, b) => a - b);
5423
+ if (sortedPorts.length === 0) return [];
5424
+ const ranges = [];
5425
+ let rangeStart = sortedPorts[0];
5426
+ let previous = sortedPorts[0];
5427
+ for (let i = 1; i < sortedPorts.length; i++) {
5428
+ const current = sortedPorts[i];
5429
+ if (current === previous + 1) {
5430
+ previous = current;
5431
+ continue;
5432
+ }
5433
+ ranges.push(rangeStart === previous ? `${rangeStart}` : `${rangeStart}:${previous}`);
5434
+ rangeStart = current;
5435
+ previous = current;
5436
+ }
5437
+ ranges.push(rangeStart === previous ? `${rangeStart}` : `${rangeStart}:${previous}`);
5438
+ return ranges;
5439
+ }
5440
+ function formatNetutilsFirewallRules(config) {
5441
+ if (!config?.netutils?.enabled || !Array.isArray(config.netutils.forwards) || config.netutils.forwards.length === 0) {
5442
+ return [];
5443
+ }
5444
+ const tcpPorts = config.netutils.forwards.filter((item) => item.protocol === "tcp").map((item) => item.host_port);
5445
+ const udpPorts = config.netutils.forwards.filter((item) => item.protocol === "udp").map((item) => item.host_port);
5446
+ const tcpRanges = formatPortRangesForIptables(tcpPorts);
5447
+ const udpRanges = formatPortRangesForIptables(udpPorts);
5448
+ if (tcpRanges.length === 0 && udpRanges.length === 0) return [];
5449
+ const lines = [];
5450
+ const addRules = (protocol, ranges) => {
5451
+ for (const chunk of chunkByMax(ranges, 15)) {
5452
+ const portSpec = chunk.join(",");
5453
+ lines.push(
5454
+ `PostUp = iptables -I DOCKER-USER -i envsync-wg -p ${protocol} -m multiport --dports ${portSpec} -j ACCEPT`
5455
+ );
5456
+ lines.push(`PostUp = iptables -A DOCKER-USER -p ${protocol} -m multiport --dports ${portSpec} -j DROP`);
5457
+ lines.push(
5458
+ `PostDown = iptables -D DOCKER-USER -p ${protocol} -m multiport --dports ${portSpec} -j DROP`
5459
+ );
5460
+ lines.push(
5461
+ `PostDown = iptables -D DOCKER-USER -i envsync-wg -p ${protocol} -m multiport --dports ${portSpec} -j ACCEPT`
5462
+ );
5463
+ }
5464
+ };
5465
+ addRules("tcp", tcpRanges);
5466
+ addRules("udp", udpRanges);
5467
+ return lines;
5468
+ }
5469
+ function tryLoadOptionalConfig() {
5470
+ try {
5471
+ return loadConfig();
5472
+ } catch {
5473
+ return null;
5474
+ }
5475
+ }
5389
5476
  function parseJsonOrDefault(value, fallback) {
5390
5477
  try {
5391
5478
  return JSON.parse(value);
@@ -5510,6 +5597,15 @@ function parseNetutilsForwardsFromInput(raw, stackName) {
5510
5597
  });
5511
5598
  return normalizeNetutilsForwards(candidates, stackName, true).forwards;
5512
5599
  }
5600
+ function buildNetutilsExposeAllForwards(stackName) {
5601
+ const candidates = NETUTILS_EXPOSE_ALL_PORTS.map((forward, index) => ({
5602
+ service: forward.service,
5603
+ service_port: forward.service_port,
5604
+ host_port: NETUTILS_EXPOSE_ALL_START_PORT + index,
5605
+ protocol: forward.protocol
5606
+ }));
5607
+ return normalizeNetutilsForwards(candidates, stackName, true).forwards;
5608
+ }
5513
5609
  function normalizeNetutilsProtocol(raw) {
5514
5610
  if (typeof raw === "string") {
5515
5611
  const protocol = raw.toLowerCase();
@@ -5635,6 +5731,8 @@ function wireguardPeerAllowedIp(state) {
5635
5731
  throw new Error("WireGuard client IP pool exhausted in subnet 10.66.0.0/24.");
5636
5732
  }
5637
5733
  function writeWireguardConfig(state) {
5734
+ const config = tryLoadOptionalConfig();
5735
+ const netutilsFirewallRules = formatNetutilsFirewallRules(config);
5638
5736
  const peersSection = state.peers.map((peer) => `
5639
5737
  [Peer]
5640
5738
  PublicKey = ${peer.public_key}
@@ -5647,6 +5745,7 @@ PostUp = iptables -A FORWARD -i envsync-wg -j ACCEPT
5647
5745
  PostDown = iptables -D FORWARD -i envsync-wg -j ACCEPT
5648
5746
  PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
5649
5747
  PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
5748
+ ${netutilsFirewallRules.join("\n")}
5650
5749
  ${peersSection}
5651
5750
  `;
5652
5751
  writeFileMaybe(WIREGUARD_CONFIG_FILE, content);
@@ -7466,13 +7565,25 @@ async function cmdSetup() {
7466
7565
  const publicObs = await ask("Expose obs.<domain> publicly (true/false)", "true") === "true";
7467
7566
  const mailpitEnabled = await ask("Enable mailpit (true/false)", "false") === "true";
7468
7567
  const netutilsEnabled = await ask("Enable WireGuard jump host access (true/false)", "false") === "true";
7469
- const netutilsForwards = netutilsEnabled ? parseNetutilsForwardsFromInput(
7470
- await ask(
7471
- "Netutils forwards (format service:service_port:host_port[:protocol], comma-separated)",
7472
- "postgres:5432:15432:tcp,keycloak:8080:18080:tcp"
7473
- ),
7474
- "envsync"
7475
- ) : [];
7568
+ const netutilsExposeAll = netutilsEnabled ? await ask("Expose all EnvSync service ports over WireGuard (true/false)", "false") === "true" : false;
7569
+ let netutilsForwards = [];
7570
+ if (netutilsEnabled) {
7571
+ if (netutilsExposeAll) {
7572
+ netutilsForwards = buildNetutilsExposeAllForwards("envsync");
7573
+ logInfo("Netutils exposes all preset (auto-generated):");
7574
+ for (const forward of netutilsForwards) {
7575
+ console.log(` ${forward.service}:${forward.service_port} -> ${forward.host_port} (${forward.protocol})`);
7576
+ }
7577
+ } else {
7578
+ netutilsForwards = parseNetutilsForwardsFromInput(
7579
+ await ask(
7580
+ "Netutils forwards (format service:service_port:host_port[:protocol], comma-separated)",
7581
+ "postgres:5432:15432:tcp,keycloak:8080:18080:tcp"
7582
+ ),
7583
+ "envsync"
7584
+ );
7585
+ }
7586
+ }
7476
7587
  const licenseServerUrl = await ask("Enterprise license server URL", DEFAULT_ENTERPRISE_LICENSE_SERVER_URL);
7477
7588
  const licenseKey = licenseServerUrl ? await ask("Enterprise license key", "") : "";
7478
7589
  const certificateBundleFile = licenseServerUrl ? "" : await ask("Enterprise certificate bundle file (optional)", "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envsync-cloud/deploy-cli",
3
- "version": "0.8.15",
3
+ "version": "0.8.16",
4
4
  "description": "CLI for self-hosted EnvSync deployment on Docker Swarm",
5
5
  "type": "module",
6
6
  "bin": {