@serve.zone/dcrouter 13.21.1 → 13.23.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.
Files changed (60) hide show
  1. package/dist_serve/bundle.js +26 -4
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.d.ts +6 -0
  4. package/dist_ts/classes.dcrouter.js +83 -5
  5. package/dist_ts/config/classes.route-config-manager.d.ts +1 -1
  6. package/dist_ts/config/classes.route-config-manager.js +2 -2
  7. package/dist_ts/db/documents/classes.ip-intelligence.doc.d.ts +25 -0
  8. package/dist_ts/db/documents/classes.ip-intelligence.doc.js +175 -0
  9. package/dist_ts/db/documents/classes.security-block-rule.doc.d.ts +17 -0
  10. package/dist_ts/db/documents/classes.security-block-rule.doc.js +124 -0
  11. package/dist_ts/db/documents/classes.security-policy-audit.doc.d.ts +11 -0
  12. package/dist_ts/db/documents/classes.security-policy-audit.doc.js +95 -0
  13. package/dist_ts/db/documents/index.d.ts +3 -0
  14. package/dist_ts/db/documents/index.js +4 -1
  15. package/dist_ts/monitoring/classes.metricsmanager.js +2 -1
  16. package/dist_ts/opsserver/handlers/config.handler.js +2 -1
  17. package/dist_ts/opsserver/handlers/remoteingress.handler.js +3 -1
  18. package/dist_ts/opsserver/handlers/security.handler.js +42 -1
  19. package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +10 -0
  20. package/dist_ts/remoteingress/classes.remoteingress-manager.js +9 -1
  21. package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +3 -0
  22. package/dist_ts/remoteingress/classes.tunnel-manager.js +23 -5
  23. package/dist_ts/security/classes.security-policy-manager.d.ts +41 -0
  24. package/dist_ts/security/classes.security-policy-manager.js +283 -0
  25. package/dist_ts/security/index.d.ts +1 -0
  26. package/dist_ts/security/index.js +2 -1
  27. package/dist_ts_apiclient/classes.remoteingress.d.ts +2 -0
  28. package/dist_ts_apiclient/classes.remoteingress.js +7 -1
  29. package/dist_ts_interfaces/data/index.d.ts +1 -0
  30. package/dist_ts_interfaces/data/index.js +2 -1
  31. package/dist_ts_interfaces/data/remoteingress.d.ts +51 -0
  32. package/dist_ts_interfaces/data/security-policy.d.ts +32 -0
  33. package/dist_ts_interfaces/data/security-policy.js +2 -0
  34. package/dist_ts_interfaces/requests/config.d.ts +1 -0
  35. package/dist_ts_interfaces/requests/index.d.ts +1 -0
  36. package/dist_ts_interfaces/requests/index.js +2 -1
  37. package/dist_ts_interfaces/requests/security-policy.d.ts +64 -0
  38. package/dist_ts_interfaces/requests/security-policy.js +2 -0
  39. package/dist_ts_web/00_commitinfo_data.js +1 -1
  40. package/dist_ts_web/elements/network/ops-view-remoteingress.d.ts +5 -0
  41. package/dist_ts_web/elements/network/ops-view-remoteingress.js +69 -1
  42. package/package.json +3 -3
  43. package/ts/00_commitinfo_data.ts +1 -1
  44. package/ts/classes.dcrouter.ts +106 -6
  45. package/ts/config/classes.route-config-manager.ts +2 -2
  46. package/ts/db/documents/classes.ip-intelligence.doc.ts +75 -0
  47. package/ts/db/documents/classes.security-block-rule.doc.ts +52 -0
  48. package/ts/db/documents/classes.security-policy-audit.doc.ts +33 -0
  49. package/ts/db/documents/index.ts +3 -0
  50. package/ts/monitoring/classes.metricsmanager.ts +2 -0
  51. package/ts/opsserver/handlers/config.handler.ts +1 -0
  52. package/ts/opsserver/handlers/remoteingress.handler.ts +2 -0
  53. package/ts/opsserver/handlers/security.handler.ts +69 -0
  54. package/ts/remoteingress/classes.remoteingress-manager.ts +15 -2
  55. package/ts/remoteingress/classes.tunnel-manager.ts +25 -5
  56. package/ts/security/classes.security-policy-manager.ts +315 -0
  57. package/ts/security/index.ts +7 -1
  58. package/ts_apiclient/classes.remoteingress.ts +6 -0
  59. package/ts_web/00_commitinfo_data.ts +1 -1
  60. package/ts_web/elements/network/ops-view-remoteingress.ts +68 -0
@@ -0,0 +1,75 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { DcRouterDb } from '../classes.dcrouter-db.js';
3
+ import type { IIpIntelligenceRecord } from '../../../ts_interfaces/data/security-policy.js';
4
+
5
+ const getDb = () => DcRouterDb.getInstance().getDb();
6
+
7
+ @plugins.smartdata.Collection(() => getDb())
8
+ export class IpIntelligenceDoc extends plugins.smartdata.SmartDataDbDoc<IpIntelligenceDoc, IpIntelligenceDoc> implements IIpIntelligenceRecord {
9
+ @plugins.smartdata.unI()
10
+ @plugins.smartdata.svDb()
11
+ public ipAddress!: string;
12
+
13
+ @plugins.smartdata.svDb()
14
+ public asn: number | null = null;
15
+
16
+ @plugins.smartdata.svDb()
17
+ public asnOrg: string | null = null;
18
+
19
+ @plugins.smartdata.svDb()
20
+ public registrantOrg: string | null = null;
21
+
22
+ @plugins.smartdata.svDb()
23
+ public registrantCountry: string | null = null;
24
+
25
+ @plugins.smartdata.svDb()
26
+ public networkRange: string | null = null;
27
+
28
+ @plugins.smartdata.svDb()
29
+ public abuseContact: string | null = null;
30
+
31
+ @plugins.smartdata.svDb()
32
+ public country: string | null = null;
33
+
34
+ @plugins.smartdata.svDb()
35
+ public countryCode: string | null = null;
36
+
37
+ @plugins.smartdata.svDb()
38
+ public city: string | null = null;
39
+
40
+ @plugins.smartdata.svDb()
41
+ public latitude: number | null = null;
42
+
43
+ @plugins.smartdata.svDb()
44
+ public longitude: number | null = null;
45
+
46
+ @plugins.smartdata.svDb()
47
+ public accuracyRadius: number | null = null;
48
+
49
+ @plugins.smartdata.svDb()
50
+ public timezone: string | null = null;
51
+
52
+ @plugins.smartdata.svDb()
53
+ public firstSeenAt: number = Date.now();
54
+
55
+ @plugins.smartdata.svDb()
56
+ public lastSeenAt: number = Date.now();
57
+
58
+ @plugins.smartdata.svDb()
59
+ public updatedAt: number = Date.now();
60
+
61
+ @plugins.smartdata.svDb()
62
+ public seenCount: number = 0;
63
+
64
+ constructor() {
65
+ super();
66
+ }
67
+
68
+ public static async findByIp(ipAddress: string): Promise<IpIntelligenceDoc | null> {
69
+ return await IpIntelligenceDoc.getInstance({ ipAddress });
70
+ }
71
+
72
+ public static async findAll(): Promise<IpIntelligenceDoc[]> {
73
+ return await IpIntelligenceDoc.getInstances({});
74
+ }
75
+ }
@@ -0,0 +1,52 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { DcRouterDb } from '../classes.dcrouter-db.js';
3
+ import type { ISecurityBlockRule, TSecurityBlockRuleMatchMode, TSecurityBlockRuleType } from '../../../ts_interfaces/data/security-policy.js';
4
+
5
+ const getDb = () => DcRouterDb.getInstance().getDb();
6
+
7
+ @plugins.smartdata.Collection(() => getDb())
8
+ export class SecurityBlockRuleDoc extends plugins.smartdata.SmartDataDbDoc<SecurityBlockRuleDoc, SecurityBlockRuleDoc> implements ISecurityBlockRule {
9
+ @plugins.smartdata.unI()
10
+ @plugins.smartdata.svDb()
11
+ public id!: string;
12
+
13
+ @plugins.smartdata.svDb()
14
+ public type!: TSecurityBlockRuleType;
15
+
16
+ @plugins.smartdata.svDb()
17
+ public value!: string;
18
+
19
+ @plugins.smartdata.svDb()
20
+ public matchMode?: TSecurityBlockRuleMatchMode;
21
+
22
+ @plugins.smartdata.svDb()
23
+ public enabled: boolean = true;
24
+
25
+ @plugins.smartdata.svDb()
26
+ public reason?: string;
27
+
28
+ @plugins.smartdata.svDb()
29
+ public createdAt: number = Date.now();
30
+
31
+ @plugins.smartdata.svDb()
32
+ public updatedAt: number = Date.now();
33
+
34
+ @plugins.smartdata.svDb()
35
+ public createdBy: string = 'system';
36
+
37
+ constructor() {
38
+ super();
39
+ }
40
+
41
+ public static async findById(id: string): Promise<SecurityBlockRuleDoc | null> {
42
+ return await SecurityBlockRuleDoc.getInstance({ id });
43
+ }
44
+
45
+ public static async findAll(): Promise<SecurityBlockRuleDoc[]> {
46
+ return await SecurityBlockRuleDoc.getInstances({});
47
+ }
48
+
49
+ public static async findEnabled(): Promise<SecurityBlockRuleDoc[]> {
50
+ return await SecurityBlockRuleDoc.getInstances({ enabled: true });
51
+ }
52
+ }
@@ -0,0 +1,33 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { DcRouterDb } from '../classes.dcrouter-db.js';
3
+ import type { ISecurityPolicyAuditEvent } from '../../../ts_interfaces/data/security-policy.js';
4
+
5
+ const getDb = () => DcRouterDb.getInstance().getDb();
6
+
7
+ @plugins.smartdata.Collection(() => getDb())
8
+ export class SecurityPolicyAuditDoc extends plugins.smartdata.SmartDataDbDoc<SecurityPolicyAuditDoc, SecurityPolicyAuditDoc> implements ISecurityPolicyAuditEvent {
9
+ @plugins.smartdata.unI()
10
+ @plugins.smartdata.svDb()
11
+ public id!: string;
12
+
13
+ @plugins.smartdata.svDb()
14
+ public action!: string;
15
+
16
+ @plugins.smartdata.svDb()
17
+ public actor!: string;
18
+
19
+ @plugins.smartdata.svDb()
20
+ public details!: Record<string, unknown>;
21
+
22
+ @plugins.smartdata.svDb()
23
+ public createdAt: number = Date.now();
24
+
25
+ constructor() {
26
+ super();
27
+ }
28
+
29
+ public static async findRecent(limit = 100): Promise<SecurityPolicyAuditDoc[]> {
30
+ const docs = await SecurityPolicyAuditDoc.getInstances({});
31
+ return docs.sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
32
+ }
33
+ }
@@ -1,6 +1,9 @@
1
1
  // Cached/TTL document classes
2
2
  export * from './classes.cached.email.js';
3
3
  export * from './classes.cached.ip.reputation.js';
4
+ export * from './classes.ip-intelligence.doc.js';
5
+ export * from './classes.security-block-rule.doc.js';
6
+ export * from './classes.security-policy-audit.doc.js';
4
7
 
5
8
  // Config document classes
6
9
  export * from './classes.route.doc.js';
@@ -725,6 +725,8 @@ export class MetricsManager {
725
725
  .slice(0, 10)
726
726
  .map(([ip, data]) => ({ ip, count: data.count, bwIn: data.bwIn, bwOut: data.bwOut }));
727
727
 
728
+ void this.dcRouter.securityPolicyManager?.observeIps([...allIPData.keys()]);
729
+
728
730
  // Build domain activity using per-IP domain request counts from Rust engine
729
731
  const connectionsByRoute = proxyMetrics.connections.byRoute();
730
732
  const throughputByRoute = proxyMetrics.throughput.byRoute();
@@ -206,6 +206,7 @@ export class ConfigHandler {
206
206
  hubDomain: riCfg?.hubDomain || null,
207
207
  tlsMode,
208
208
  connectedEdgeIps,
209
+ performance: riCfg?.performance,
209
210
  };
210
211
 
211
212
  return {
@@ -29,6 +29,7 @@ export class RemoteIngressHandler {
29
29
  ...e,
30
30
  secret: '********', // Never expose secrets via API
31
31
  effectiveListenPorts: manager.getEffectiveListenPorts(e),
32
+ effectiveListenPortsUdp: manager.getEffectiveListenPortsUdp(e),
32
33
  manualPorts: breakdown.manual,
33
34
  derivedPorts: breakdown.derived,
34
35
  };
@@ -133,6 +134,7 @@ export class RemoteIngressHandler {
133
134
  ...edge,
134
135
  secret: '********',
135
136
  effectiveListenPorts: manager.getEffectiveListenPorts(edge),
137
+ effectiveListenPortsUdp: manager.getEffectiveListenPortsUdp(edge),
136
138
  manualPorts: breakdown.manual,
137
139
  derivedPorts: breakdown.derived,
138
140
  },
@@ -157,6 +157,75 @@ export class SecurityHandler {
157
157
  }
158
158
  )
159
159
  );
160
+
161
+ router.addTypedHandler(
162
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityBlockRules>(
163
+ 'listSecurityBlockRules',
164
+ async () => {
165
+ const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
166
+ return { rules: manager ? await manager.listBlockRules() : [] };
167
+ },
168
+ ),
169
+ );
170
+
171
+ router.addTypedHandler(
172
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListIpIntelligence>(
173
+ 'listIpIntelligence',
174
+ async () => {
175
+ const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
176
+ return { records: manager ? await manager.listIpIntelligence() : [] };
177
+ },
178
+ ),
179
+ );
180
+
181
+ const adminRouter = this.opsServerRef.adminRouter;
182
+
183
+ adminRouter.addTypedHandler(
184
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecurityBlockRule>(
185
+ 'createSecurityBlockRule',
186
+ async (dataArg) => {
187
+ const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
188
+ if (!manager) return { success: false, message: 'Security policy manager not initialized' };
189
+ const rule = await manager.createBlockRule({
190
+ type: dataArg.type,
191
+ value: dataArg.value,
192
+ matchMode: dataArg.matchMode,
193
+ reason: dataArg.reason,
194
+ enabled: dataArg.enabled,
195
+ }, dataArg.identity.userId);
196
+ return { success: true, rule };
197
+ },
198
+ ),
199
+ );
200
+
201
+ adminRouter.addTypedHandler(
202
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecurityBlockRule>(
203
+ 'updateSecurityBlockRule',
204
+ async (dataArg) => {
205
+ const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
206
+ if (!manager) return { success: false, message: 'Security policy manager not initialized' };
207
+ const rule = await manager.updateBlockRule(dataArg.id, {
208
+ value: dataArg.value,
209
+ matchMode: dataArg.matchMode,
210
+ reason: dataArg.reason,
211
+ enabled: dataArg.enabled,
212
+ }, dataArg.identity.userId);
213
+ return rule ? { success: true, rule } : { success: false, message: 'Rule not found' };
214
+ },
215
+ ),
216
+ );
217
+
218
+ adminRouter.addTypedHandler(
219
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecurityBlockRule>(
220
+ 'deleteSecurityBlockRule',
221
+ async (dataArg) => {
222
+ const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
223
+ if (!manager) return { success: false, message: 'Security policy manager not initialized' };
224
+ const success = await manager.deleteBlockRule(dataArg.id, dataArg.identity.userId);
225
+ return { success, message: success ? undefined : 'Rule not found' };
226
+ },
227
+ ),
228
+ );
160
229
  }
161
230
 
162
231
  private async collectSecurityMetrics(): Promise<{
@@ -2,6 +2,10 @@ import * as plugins from '../plugins.js';
2
2
  import type { IRemoteIngress, IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
3
3
  import { RemoteIngressEdgeDoc } from '../db/index.js';
4
4
 
5
+ interface IRemoteIngressFirewallConfig {
6
+ blockedIps?: string[];
7
+ }
8
+
5
9
  /**
6
10
  * Flatten a port range (number | number[] | Array<{from, to}>) to a sorted unique number array.
7
11
  */
@@ -31,6 +35,7 @@ function extractPorts(portRange: number | Array<number | { from: number; to: num
31
35
  export class RemoteIngressManager {
32
36
  private edges: Map<string, IRemoteIngress> = new Map();
33
37
  private routes: IDcRouterRouteConfig[] = [];
38
+ private firewallConfig?: IRemoteIngressFirewallConfig;
34
39
 
35
40
  constructor() {
36
41
  }
@@ -69,6 +74,13 @@ export class RemoteIngressManager {
69
74
  this.routes = routes;
70
75
  }
71
76
 
77
+ /**
78
+ * Set the full desired firewall snapshot pushed to all edges.
79
+ */
80
+ public setFirewallConfig(firewallConfig?: IRemoteIngressFirewallConfig): void {
81
+ this.firewallConfig = firewallConfig;
82
+ }
83
+
72
84
  /**
73
85
  * Derive listen ports for an edge from routes tagged with remoteIngress.enabled.
74
86
  * When a route specifies edgeFilter, only edges whose id or tags match get that route's ports.
@@ -305,8 +317,8 @@ export class RemoteIngressManager {
305
317
  * Get the list of allowed edges (enabled only) for the Rust hub.
306
318
  * Includes listenPortsUdp when routes with transport 'udp' or 'all' are present.
307
319
  */
308
- public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[] }> {
309
- const result: Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[] }> = [];
320
+ public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig }> {
321
+ const result: Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig }> = [];
310
322
  for (const edge of this.edges.values()) {
311
323
  if (edge.enabled) {
312
324
  const listenPortsUdp = this.getEffectiveListenPortsUdp(edge);
@@ -315,6 +327,7 @@ export class RemoteIngressManager {
315
327
  secret: edge.secret,
316
328
  listenPorts: this.getEffectiveListenPorts(edge),
317
329
  ...(listenPortsUdp.length > 0 ? { listenPortsUdp } : {}),
330
+ ...(this.firewallConfig ? { firewallConfig: this.firewallConfig } : {}),
318
331
  });
319
332
  }
320
333
  }
@@ -9,6 +9,7 @@ export interface ITunnelManagerConfig {
9
9
  certPem?: string;
10
10
  keyPem?: string;
11
11
  };
12
+ performance?: import('../../ts_interfaces/data/remoteingress.js').IRemoteIngressPerformanceConfig;
12
13
  }
13
14
 
14
15
  /**
@@ -20,6 +21,7 @@ export class TunnelManager {
20
21
  private config: ITunnelManagerConfig;
21
22
  private edgeStatuses: Map<string, IRemoteIngressStatus> = new Map();
22
23
  private reconcileInterval: ReturnType<typeof setInterval> | null = null;
24
+ private syncChain: Promise<void> = Promise.resolve();
23
25
 
24
26
  constructor(manager: RemoteIngressManager, config: ITunnelManagerConfig = {}) {
25
27
  this.manager = manager;
@@ -66,7 +68,8 @@ export class TunnelManager {
66
68
  tunnelPort: this.config.tunnelPort ?? 8443,
67
69
  targetHost: this.config.targetHost ?? '127.0.0.1',
68
70
  tls: this.config.tls,
69
- });
71
+ ...(this.config.performance ? { performance: this.config.performance } : {}),
72
+ } as any);
70
73
 
71
74
  // Send allowed edges to the hub
72
75
  await this.syncAllowedEdges();
@@ -107,20 +110,23 @@ export class TunnelManager {
107
110
  if (existing) {
108
111
  existing.activeTunnels = rustEdge.activeStreams;
109
112
  existing.lastHeartbeat = Date.now();
113
+ this.applyRustStatus(existing, rustEdge);
110
114
  // Update peer address if available from Rust hub
111
115
  if (rustEdge.peerAddr) {
112
116
  existing.publicIp = rustEdge.peerAddr;
113
117
  }
114
118
  } else {
115
119
  // Missed edgeConnected event — add entry
116
- this.edgeStatuses.set(rustEdge.edgeId, {
120
+ const status: IRemoteIngressStatus = {
117
121
  edgeId: rustEdge.edgeId,
118
122
  connected: true,
119
123
  publicIp: rustEdge.peerAddr || null,
120
124
  activeTunnels: rustEdge.activeStreams,
121
125
  lastHeartbeat: Date.now(),
122
126
  connectedAt: rustEdge.connectedAt * 1000,
123
- });
127
+ };
128
+ this.applyRustStatus(status, rustEdge);
129
+ this.edgeStatuses.set(rustEdge.edgeId, status);
124
130
  }
125
131
  }
126
132
 
@@ -137,8 +143,22 @@ export class TunnelManager {
137
143
  * Call this after creating/deleting/updating edges.
138
144
  */
139
145
  public async syncAllowedEdges(): Promise<void> {
140
- const edges = this.manager.getAllowedEdges();
141
- await this.hub.updateAllowedEdges(edges);
146
+ const run = this.syncChain.catch(() => {}).then(async () => {
147
+ const edges = this.manager.getAllowedEdges();
148
+ await this.hub.updateAllowedEdges(edges as any);
149
+ });
150
+ this.syncChain = run;
151
+ await run;
152
+ }
153
+
154
+ private applyRustStatus(status: IRemoteIngressStatus, rustEdge: any): void {
155
+ status.transportMode = rustEdge.transportMode;
156
+ status.fallbackUsed = rustEdge.fallbackUsed;
157
+ status.performance = rustEdge.performance;
158
+ status.flowControl = rustEdge.flowControl;
159
+ status.queues = rustEdge.queues;
160
+ status.traffic = rustEdge.traffic;
161
+ status.udp = rustEdge.udp;
142
162
  }
143
163
 
144
164
  /**