@serve.zone/dcrouter 11.14.0 → 11.16.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/dist_serve/bundle.js +5 -9
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/cache/classes.cache.cleaner.d.ts +47 -0
- package/dist_ts/cache/classes.cache.cleaner.js +130 -0
- package/dist_ts/cache/classes.cached.document.d.ts +76 -0
- package/dist_ts/cache/classes.cached.document.js +100 -0
- package/dist_ts/cache/classes.cachedb.d.ts +60 -0
- package/dist_ts/cache/classes.cachedb.js +126 -0
- package/dist_ts/cache/documents/classes.cached.email.d.ts +125 -0
- package/dist_ts/cache/documents/classes.cached.email.js +337 -0
- package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +119 -0
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +323 -0
- package/dist_ts/cache/documents/index.d.ts +2 -0
- package/dist_ts/cache/documents/index.js +3 -0
- package/dist_ts/cache/index.d.ts +4 -0
- package/dist_ts/cache/index.js +7 -0
- package/dist_ts/classes.cert-provision-scheduler.d.ts +54 -0
- package/dist_ts/classes.cert-provision-scheduler.js +118 -0
- package/dist_ts/classes.dcrouter.d.ts +399 -0
- package/dist_ts/classes.dcrouter.js +1697 -0
- package/dist_ts/classes.storage-cert-manager.d.ts +18 -0
- package/dist_ts/classes.storage-cert-manager.js +43 -0
- package/dist_ts/config/classes.api-token-manager.d.ts +46 -0
- package/dist_ts/config/classes.api-token-manager.js +150 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +38 -0
- package/dist_ts/config/classes.route-config-manager.js +257 -0
- package/dist_ts/config/index.d.ts +3 -0
- package/dist_ts/config/index.js +5 -0
- package/dist_ts/config/validator.d.ts +104 -0
- package/dist_ts/config/validator.js +152 -0
- package/dist_ts/errors/base.errors.d.ts +224 -0
- package/dist_ts/errors/base.errors.js +320 -0
- package/dist_ts/errors/error-handler.d.ts +98 -0
- package/dist_ts/errors/error-handler.js +282 -0
- package/dist_ts/errors/error.codes.d.ts +115 -0
- package/dist_ts/errors/error.codes.js +136 -0
- package/dist_ts/errors/index.d.ts +54 -0
- package/dist_ts/errors/index.js +136 -0
- package/dist_ts/errors/reputation.errors.d.ts +183 -0
- package/dist_ts/errors/reputation.errors.js +292 -0
- package/dist_ts/http3/http3-route-augmentation.d.ts +50 -0
- package/dist_ts/http3/http3-route-augmentation.js +98 -0
- package/dist_ts/http3/index.d.ts +1 -0
- package/dist_ts/http3/index.js +2 -0
- package/dist_ts/index.d.ts +8 -0
- package/dist_ts/index.js +29 -0
- package/dist_ts/logger.d.ts +21 -0
- package/dist_ts/logger.js +81 -0
- package/dist_ts/monitoring/classes.metricscache.d.ts +32 -0
- package/dist_ts/monitoring/classes.metricscache.js +63 -0
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +184 -0
- package/dist_ts/monitoring/classes.metricsmanager.js +744 -0
- package/dist_ts/monitoring/index.d.ts +1 -0
- package/dist_ts/monitoring/index.js +2 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +38 -0
- package/dist_ts/opsserver/classes.opsserver.js +87 -0
- package/dist_ts/opsserver/handlers/admin.handler.d.ts +31 -0
- package/dist_ts/opsserver/handlers/admin.handler.js +180 -0
- package/dist_ts/opsserver/handlers/api-token.handler.d.ts +6 -0
- package/dist_ts/opsserver/handlers/api-token.handler.js +62 -0
- package/dist_ts/opsserver/handlers/certificate.handler.d.ts +32 -0
- package/dist_ts/opsserver/handlers/certificate.handler.js +421 -0
- package/dist_ts/opsserver/handlers/config.handler.d.ts +7 -0
- package/dist_ts/opsserver/handlers/config.handler.js +192 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +30 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +227 -0
- package/dist_ts/opsserver/handlers/index.d.ts +12 -0
- package/dist_ts/opsserver/handlers/index.js +13 -0
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +25 -0
- package/dist_ts/opsserver/handlers/logs.handler.js +256 -0
- package/dist_ts/opsserver/handlers/radius.handler.d.ts +6 -0
- package/dist_ts/opsserver/handlers/radius.handler.js +295 -0
- package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +6 -0
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +156 -0
- package/dist_ts/opsserver/handlers/route-management.handler.d.ts +14 -0
- package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
- package/dist_ts/opsserver/handlers/security.handler.d.ts +9 -0
- package/dist_ts/opsserver/handlers/security.handler.js +233 -0
- package/dist_ts/opsserver/handlers/stats.handler.d.ts +11 -0
- package/dist_ts/opsserver/handlers/stats.handler.js +403 -0
- package/dist_ts/opsserver/handlers/vpn.handler.d.ts +6 -0
- package/dist_ts/opsserver/handlers/vpn.handler.js +197 -0
- package/dist_ts/opsserver/helpers/guards.d.ts +27 -0
- package/dist_ts/opsserver/helpers/guards.js +43 -0
- package/dist_ts/opsserver/index.d.ts +1 -0
- package/dist_ts/opsserver/index.js +2 -0
- package/dist_ts/paths.d.ts +26 -0
- package/dist_ts/paths.js +45 -0
- package/dist_ts/plugins.d.ts +81 -0
- package/dist_ts/plugins.js +115 -0
- package/dist_ts/radius/classes.accounting.manager.d.ts +231 -0
- package/dist_ts/radius/classes.accounting.manager.js +462 -0
- package/dist_ts/radius/classes.radius.server.d.ts +171 -0
- package/dist_ts/radius/classes.radius.server.js +386 -0
- package/dist_ts/radius/classes.vlan.manager.d.ts +128 -0
- package/dist_ts/radius/classes.vlan.manager.js +279 -0
- package/dist_ts/radius/index.d.ts +13 -0
- package/dist_ts/radius/index.js +14 -0
- package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +94 -0
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +271 -0
- package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +59 -0
- package/dist_ts/remoteingress/classes.tunnel-manager.js +165 -0
- package/dist_ts/remoteingress/index.d.ts +2 -0
- package/dist_ts/remoteingress/index.js +3 -0
- package/dist_ts/security/classes.contentscanner.d.ts +164 -0
- package/dist_ts/security/classes.contentscanner.js +642 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +160 -0
- package/dist_ts/security/classes.ipreputationchecker.js +537 -0
- package/dist_ts/security/classes.securitylogger.d.ts +144 -0
- package/dist_ts/security/classes.securitylogger.js +235 -0
- package/dist_ts/security/index.d.ts +3 -0
- package/dist_ts/security/index.js +4 -0
- package/dist_ts/sms/classes.smsservice.d.ts +15 -0
- package/dist_ts/sms/classes.smsservice.js +72 -0
- package/dist_ts/sms/config/sms.config.d.ts +93 -0
- package/dist_ts/sms/config/sms.config.js +2 -0
- package/dist_ts/sms/config/sms.schema.d.ts +5 -0
- package/dist_ts/sms/config/sms.schema.js +121 -0
- package/dist_ts/sms/index.d.ts +1 -0
- package/dist_ts/sms/index.js +2 -0
- package/dist_ts/storage/classes.storagemanager.d.ts +83 -0
- package/dist_ts/storage/classes.storagemanager.js +348 -0
- package/dist_ts/storage/index.d.ts +1 -0
- package/dist_ts/storage/index.js +3 -0
- package/dist_ts/vpn/classes.vpn-manager.d.ts +129 -0
- package/dist_ts/vpn/classes.vpn-manager.js +329 -0
- package/dist_ts/vpn/index.d.ts +1 -0
- package/dist_ts/vpn/index.js +2 -0
- package/dist_ts_apiclient/classes.apitoken.d.ts +41 -0
- package/dist_ts_apiclient/classes.apitoken.js +115 -0
- package/dist_ts_apiclient/classes.certificate.d.ts +57 -0
- package/dist_ts_apiclient/classes.certificate.js +69 -0
- package/dist_ts_apiclient/classes.config.d.ts +7 -0
- package/dist_ts_apiclient/classes.config.js +11 -0
- package/dist_ts_apiclient/classes.dcrouterapiclient.d.ts +41 -0
- package/dist_ts_apiclient/classes.dcrouterapiclient.js +81 -0
- package/dist_ts_apiclient/classes.email.d.ts +30 -0
- package/dist_ts_apiclient/classes.email.js +52 -0
- package/dist_ts_apiclient/classes.logs.d.ts +21 -0
- package/dist_ts_apiclient/classes.logs.js +14 -0
- package/dist_ts_apiclient/classes.radius.d.ts +59 -0
- package/dist_ts_apiclient/classes.radius.js +95 -0
- package/dist_ts_apiclient/classes.remoteingress.d.ts +54 -0
- package/dist_ts_apiclient/classes.remoteingress.js +136 -0
- package/dist_ts_apiclient/classes.route.d.ts +42 -0
- package/dist_ts_apiclient/classes.route.js +154 -0
- package/dist_ts_apiclient/classes.stats.d.ts +47 -0
- package/dist_ts_apiclient/classes.stats.js +38 -0
- package/dist_ts_apiclient/index.d.ts +10 -0
- package/dist_ts_apiclient/index.js +14 -0
- package/dist_ts_apiclient/plugins.d.ts +3 -0
- package/dist_ts_apiclient/plugins.js +5 -0
- package/dist_ts_interfaces/data/remoteingress.d.ts +2 -0
- package/dist_ts_interfaces/data/vpn.d.ts +1 -2
- package/dist_ts_interfaces/requests/vpn.d.ts +1 -1
- package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/appstate.d.ts +238 -0
- package/dist_ts_web/appstate.js +1174 -0
- package/dist_ts_web/elements/index.d.ts +13 -0
- package/dist_ts_web/elements/index.js +14 -0
- package/dist_ts_web/elements/ops-dashboard.d.ts +23 -0
- package/dist_ts_web/elements/ops-dashboard.js +323 -0
- package/dist_ts_web/elements/ops-view-apitokens.d.ts +13 -0
- package/dist_ts_web/elements/ops-view-apitokens.js +371 -0
- package/dist_ts_web/elements/ops-view-certificates.d.ts +22 -0
- package/dist_ts_web/elements/ops-view-certificates.js +528 -0
- package/dist_ts_web/elements/ops-view-config.d.ts +19 -0
- package/dist_ts_web/elements/ops-view-config.js +339 -0
- package/dist_ts_web/elements/ops-view-emails.d.ts +21 -0
- package/dist_ts_web/elements/ops-view-emails.js +165 -0
- package/dist_ts_web/elements/ops-view-logs.d.ts +13 -0
- package/dist_ts_web/elements/ops-view-logs.js +159 -0
- package/dist_ts_web/elements/ops-view-network.d.ts +71 -0
- package/dist_ts_web/elements/ops-view-network.js +764 -0
- package/dist_ts_web/elements/ops-view-overview.d.ts +22 -0
- package/dist_ts_web/elements/ops-view-overview.js +456 -0
- package/dist_ts_web/elements/ops-view-remoteingress.d.ts +20 -0
- package/dist_ts_web/elements/ops-view-remoteingress.js +494 -0
- package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-routes.js +404 -0
- package/dist_ts_web/elements/ops-view-security.d.ts +21 -0
- package/dist_ts_web/elements/ops-view-security.js +574 -0
- package/dist_ts_web/elements/ops-view-vpn.d.ts +14 -0
- package/dist_ts_web/elements/ops-view-vpn.js +365 -0
- package/dist_ts_web/elements/shared/css.d.ts +1 -0
- package/dist_ts_web/elements/shared/css.js +10 -0
- package/dist_ts_web/elements/shared/index.d.ts +2 -0
- package/dist_ts_web/elements/shared/index.js +3 -0
- package/dist_ts_web/elements/shared/ops-sectionheading.d.ts +5 -0
- package/dist_ts_web/elements/shared/ops-sectionheading.js +82 -0
- package/dist_ts_web/index.d.ts +1 -0
- package/dist_ts_web/index.js +10 -0
- package/dist_ts_web/plugins.d.ts +6 -0
- package/dist_ts_web/plugins.js +11 -0
- package/dist_ts_web/router.d.ts +19 -0
- package/dist_ts_web/router.js +91 -0
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +51 -20
- package/ts/config/classes.route-config-manager.ts +7 -6
- package/ts/opsserver/handlers/vpn.handler.ts +3 -5
- package/ts/vpn/classes.vpn-manager.ts +68 -19
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +2 -2
- package/ts_web/elements/ops-view-vpn.ts +5 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/dcrouter",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.16.0",
|
|
5
5
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@push.rocks/smartrx": "^3.0.10",
|
|
60
60
|
"@push.rocks/smartstate": "^2.3.0",
|
|
61
61
|
"@push.rocks/smartunique": "^3.0.9",
|
|
62
|
-
"@push.rocks/smartvpn": "1.
|
|
62
|
+
"@push.rocks/smartvpn": "1.14.0",
|
|
63
63
|
"@push.rocks/taskbuffer": "^8.0.2",
|
|
64
64
|
"@serve.zone/catalog": "^2.9.0",
|
|
65
65
|
"@serve.zone/interfaces": "^5.3.0",
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -206,8 +206,21 @@ export interface IDcRouterOptions {
|
|
|
206
206
|
dns?: string[];
|
|
207
207
|
/** Server endpoint hostname for client configs (e.g. 'vpn.example.com') */
|
|
208
208
|
serverEndpoint?: string;
|
|
209
|
-
/**
|
|
210
|
-
|
|
209
|
+
/** Pre-defined VPN clients created on startup */
|
|
210
|
+
clients?: Array<{
|
|
211
|
+
clientId: string;
|
|
212
|
+
serverDefinedClientTags?: string[];
|
|
213
|
+
description?: string;
|
|
214
|
+
}>;
|
|
215
|
+
/** Destination routing policy for VPN client traffic.
|
|
216
|
+
* Default in socket mode: { default: 'forceTarget', target: '127.0.0.1' } (all traffic → SmartProxy).
|
|
217
|
+
* Default in tun mode: not set (all traffic passes through). */
|
|
218
|
+
destinationPolicy?: {
|
|
219
|
+
default: 'forceTarget' | 'block' | 'allow';
|
|
220
|
+
target?: string;
|
|
221
|
+
allowList?: string[];
|
|
222
|
+
blockList?: string[];
|
|
223
|
+
};
|
|
211
224
|
};
|
|
212
225
|
}
|
|
213
226
|
|
|
@@ -453,7 +466,14 @@ export class DcRouter {
|
|
|
453
466
|
() => this.getConstructorRoutes(),
|
|
454
467
|
() => this.smartProxy,
|
|
455
468
|
() => this.options.http3,
|
|
456
|
-
|
|
469
|
+
this.options.vpnConfig?.enabled
|
|
470
|
+
? (tags?: string[]) => {
|
|
471
|
+
if (tags?.length && this.vpnManager) {
|
|
472
|
+
return this.vpnManager.getClientIpsForServerDefinedTags(tags);
|
|
473
|
+
}
|
|
474
|
+
return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
|
|
475
|
+
}
|
|
476
|
+
: undefined,
|
|
457
477
|
);
|
|
458
478
|
this.apiTokenManager = new ApiTokenManager(this.storageManager);
|
|
459
479
|
await this.apiTokenManager.initialize();
|
|
@@ -664,9 +684,8 @@ export class DcRouter {
|
|
|
664
684
|
if (this.vpnManager && this.options.vpnConfig?.enabled) {
|
|
665
685
|
const subnet = this.vpnManager.getSubnet();
|
|
666
686
|
const wgPort = this.options.vpnConfig.wgListenPort ?? 51820;
|
|
667
|
-
const mode = this.vpnManager.forwardingMode;
|
|
668
687
|
const clientCount = this.vpnManager.listClients().length;
|
|
669
|
-
logger.log('info', `VPN Service:
|
|
688
|
+
logger.log('info', `VPN Service: subnet=${subnet}, wg=:${wgPort}, clients=${clientCount}`);
|
|
670
689
|
}
|
|
671
690
|
|
|
672
691
|
// Remote Ingress summary
|
|
@@ -950,19 +969,14 @@ export class DcRouter {
|
|
|
950
969
|
smartProxyConfig.proxyIPs = ['127.0.0.1'];
|
|
951
970
|
}
|
|
952
971
|
|
|
953
|
-
//
|
|
954
|
-
// on outbound connections to SmartProxy to preserve VPN client tunnel IPs.
|
|
972
|
+
// VPN uses socket mode with PP v2 — SmartProxy must accept proxy protocol from localhost
|
|
955
973
|
if (this.options.vpnConfig?.enabled) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
if (!smartProxyConfig.proxyIPs.includes('127.0.0.1')) {
|
|
964
|
-
smartProxyConfig.proxyIPs.push('127.0.0.1');
|
|
965
|
-
}
|
|
974
|
+
smartProxyConfig.acceptProxyProtocol = true;
|
|
975
|
+
if (!smartProxyConfig.proxyIPs) {
|
|
976
|
+
smartProxyConfig.proxyIPs = [];
|
|
977
|
+
}
|
|
978
|
+
if (!smartProxyConfig.proxyIPs.includes('127.0.0.1')) {
|
|
979
|
+
smartProxyConfig.proxyIPs.push('127.0.0.1');
|
|
966
980
|
}
|
|
967
981
|
}
|
|
968
982
|
|
|
@@ -2085,7 +2099,12 @@ export class DcRouter {
|
|
|
2085
2099
|
wgListenPort: this.options.vpnConfig.wgListenPort,
|
|
2086
2100
|
dns: this.options.vpnConfig.dns,
|
|
2087
2101
|
serverEndpoint: this.options.vpnConfig.serverEndpoint,
|
|
2088
|
-
|
|
2102
|
+
initialClients: this.options.vpnConfig.clients,
|
|
2103
|
+
destinationPolicy: this.options.vpnConfig.destinationPolicy,
|
|
2104
|
+
onClientChanged: () => {
|
|
2105
|
+
// Re-apply routes so tag-based ipAllowLists get updated
|
|
2106
|
+
this.routeConfigManager?.applyRoutes();
|
|
2107
|
+
},
|
|
2089
2108
|
});
|
|
2090
2109
|
|
|
2091
2110
|
await this.vpnManager.start();
|
|
@@ -2104,11 +2123,23 @@ export class DcRouter {
|
|
|
2104
2123
|
if (dcrouterRoute.vpn?.required) {
|
|
2105
2124
|
injectedCount++;
|
|
2106
2125
|
const existing = route.security?.ipAllowList || [];
|
|
2126
|
+
|
|
2127
|
+
let vpnAllowList: string[];
|
|
2128
|
+
if (dcrouterRoute.vpn.allowedServerDefinedClientTags?.length && this.vpnManager) {
|
|
2129
|
+
// Tag-based: only specific client IPs
|
|
2130
|
+
vpnAllowList = this.vpnManager.getClientIpsForServerDefinedTags(
|
|
2131
|
+
dcrouterRoute.vpn.allowedServerDefinedClientTags,
|
|
2132
|
+
);
|
|
2133
|
+
} else {
|
|
2134
|
+
// No tags specified: entire VPN subnet
|
|
2135
|
+
vpnAllowList = [vpnSubnet];
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2107
2138
|
return {
|
|
2108
2139
|
...route,
|
|
2109
2140
|
security: {
|
|
2110
2141
|
...route.security,
|
|
2111
|
-
ipAllowList: [...existing,
|
|
2142
|
+
ipAllowList: [...existing, ...vpnAllowList],
|
|
2112
2143
|
},
|
|
2113
2144
|
};
|
|
2114
2145
|
}
|
|
@@ -2116,7 +2147,7 @@ export class DcRouter {
|
|
|
2116
2147
|
});
|
|
2117
2148
|
|
|
2118
2149
|
if (injectedCount > 0) {
|
|
2119
|
-
logger.log('info', `VPN: Injected ipAllowList
|
|
2150
|
+
logger.log('info', `VPN: Injected ipAllowList into ${injectedCount} VPN-protected route(s)`);
|
|
2120
2151
|
}
|
|
2121
2152
|
|
|
2122
2153
|
return result;
|
|
@@ -23,7 +23,7 @@ export class RouteConfigManager {
|
|
|
23
23
|
private getHardcodedRoutes: () => plugins.smartproxy.IRouteConfig[],
|
|
24
24
|
private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
|
|
25
25
|
private getHttp3Config?: () => IHttp3Config | undefined,
|
|
26
|
-
private
|
|
26
|
+
private getVpnAllowList?: (tags?: string[]) => string[],
|
|
27
27
|
) {}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -246,7 +246,7 @@ export class RouteConfigManager {
|
|
|
246
246
|
// Private: apply merged routes to SmartProxy
|
|
247
247
|
// =========================================================================
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
public async applyRoutes(): Promise<void> {
|
|
250
250
|
const smartProxy = this.getSmartProxy();
|
|
251
251
|
if (!smartProxy) return;
|
|
252
252
|
|
|
@@ -262,9 +262,9 @@ export class RouteConfigManager {
|
|
|
262
262
|
enabledRoutes.push(route);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
// Add enabled programmatic routes (with HTTP/3
|
|
265
|
+
// Add enabled programmatic routes (with HTTP/3 and VPN augmentation)
|
|
266
266
|
const http3Config = this.getHttp3Config?.();
|
|
267
|
-
const
|
|
267
|
+
const vpnAllowList = this.getVpnAllowList;
|
|
268
268
|
for (const stored of this.storedRoutes.values()) {
|
|
269
269
|
if (stored.enabled) {
|
|
270
270
|
let route = stored.route;
|
|
@@ -272,15 +272,16 @@ export class RouteConfigManager {
|
|
|
272
272
|
route = augmentRouteWithHttp3(route, { enabled: true, ...http3Config });
|
|
273
273
|
}
|
|
274
274
|
// Inject VPN security for programmatic routes with vpn.required
|
|
275
|
-
if (
|
|
275
|
+
if (vpnAllowList) {
|
|
276
276
|
const dcRoute = route as IDcRouterRouteConfig;
|
|
277
277
|
if (dcRoute.vpn?.required) {
|
|
278
278
|
const existing = route.security?.ipAllowList || [];
|
|
279
|
+
const allowList = vpnAllowList(dcRoute.vpn.allowedServerDefinedClientTags);
|
|
279
280
|
route = {
|
|
280
281
|
...route,
|
|
281
282
|
security: {
|
|
282
283
|
...route.security,
|
|
283
|
-
ipAllowList: [...existing,
|
|
284
|
+
ipAllowList: [...existing, ...allowList],
|
|
284
285
|
},
|
|
285
286
|
};
|
|
286
287
|
}
|
|
@@ -25,7 +25,7 @@ export class VpnHandler {
|
|
|
25
25
|
const clients = manager.listClients().map((c) => ({
|
|
26
26
|
clientId: c.clientId,
|
|
27
27
|
enabled: c.enabled,
|
|
28
|
-
|
|
28
|
+
serverDefinedClientTags: c.serverDefinedClientTags,
|
|
29
29
|
description: c.description,
|
|
30
30
|
assignedIp: c.assignedIp,
|
|
31
31
|
createdAt: c.createdAt,
|
|
@@ -48,7 +48,6 @@ export class VpnHandler {
|
|
|
48
48
|
return {
|
|
49
49
|
status: {
|
|
50
50
|
running: false,
|
|
51
|
-
forwardingMode: 'socket' as const,
|
|
52
51
|
subnet: vpnConfig?.subnet || '10.8.0.0/24',
|
|
53
52
|
wgListenPort: vpnConfig?.wgListenPort ?? 51820,
|
|
54
53
|
serverPublicKeys: null,
|
|
@@ -62,7 +61,6 @@ export class VpnHandler {
|
|
|
62
61
|
return {
|
|
63
62
|
status: {
|
|
64
63
|
running: manager.running,
|
|
65
|
-
forwardingMode: manager.forwardingMode,
|
|
66
64
|
subnet: manager.getSubnet(),
|
|
67
65
|
wgListenPort: vpnConfig?.wgListenPort ?? 51820,
|
|
68
66
|
serverPublicKeys: manager.getServerPublicKeys(),
|
|
@@ -89,7 +87,7 @@ export class VpnHandler {
|
|
|
89
87
|
try {
|
|
90
88
|
const bundle = await manager.createClient({
|
|
91
89
|
clientId: dataArg.clientId,
|
|
92
|
-
|
|
90
|
+
serverDefinedClientTags: dataArg.serverDefinedClientTags,
|
|
93
91
|
description: dataArg.description,
|
|
94
92
|
});
|
|
95
93
|
|
|
@@ -98,7 +96,7 @@ export class VpnHandler {
|
|
|
98
96
|
client: {
|
|
99
97
|
clientId: bundle.entry.clientId,
|
|
100
98
|
enabled: bundle.entry.enabled ?? true,
|
|
101
|
-
|
|
99
|
+
serverDefinedClientTags: bundle.entry.serverDefinedClientTags,
|
|
102
100
|
description: bundle.entry.description,
|
|
103
101
|
assignedIp: bundle.entry.assignedIp,
|
|
104
102
|
createdAt: Date.now(),
|
|
@@ -14,8 +14,21 @@ export interface IVpnManagerConfig {
|
|
|
14
14
|
dns?: string[];
|
|
15
15
|
/** Server endpoint hostname for client configs (e.g. 'vpn.example.com') */
|
|
16
16
|
serverEndpoint?: string;
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/** Pre-defined VPN clients created on startup (idempotent — skips already-persisted clients) */
|
|
18
|
+
initialClients?: Array<{
|
|
19
|
+
clientId: string;
|
|
20
|
+
serverDefinedClientTags?: string[];
|
|
21
|
+
description?: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** Called when clients are created/deleted/toggled — triggers route re-application */
|
|
24
|
+
onClientChanged?: () => void;
|
|
25
|
+
/** Destination routing policy override. Default: forceTarget to 127.0.0.1 */
|
|
26
|
+
destinationPolicy?: {
|
|
27
|
+
default: 'forceTarget' | 'block' | 'allow';
|
|
28
|
+
target?: string;
|
|
29
|
+
allowList?: string[];
|
|
30
|
+
blockList?: string[];
|
|
31
|
+
};
|
|
19
32
|
}
|
|
20
33
|
|
|
21
34
|
interface IPersistedServerKeys {
|
|
@@ -28,7 +41,7 @@ interface IPersistedServerKeys {
|
|
|
28
41
|
interface IPersistedClient {
|
|
29
42
|
clientId: string;
|
|
30
43
|
enabled: boolean;
|
|
31
|
-
|
|
44
|
+
serverDefinedClientTags?: string[];
|
|
32
45
|
description?: string;
|
|
33
46
|
assignedIp?: string;
|
|
34
47
|
noisePublicKey: string;
|
|
@@ -36,6 +49,8 @@ interface IPersistedClient {
|
|
|
36
49
|
createdAt: number;
|
|
37
50
|
updatedAt: number;
|
|
38
51
|
expiresAt?: string;
|
|
52
|
+
/** @deprecated Legacy field — migrated to serverDefinedClientTags on load */
|
|
53
|
+
tags?: string[];
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
/**
|
|
@@ -48,19 +63,10 @@ export class VpnManager {
|
|
|
48
63
|
private vpnServer?: plugins.smartvpn.VpnServer;
|
|
49
64
|
private clients: Map<string, IPersistedClient> = new Map();
|
|
50
65
|
private serverKeys?: IPersistedServerKeys;
|
|
51
|
-
private _forwardingMode: 'tun' | 'socket';
|
|
52
66
|
|
|
53
67
|
constructor(storageManager: StorageManager, config: IVpnManagerConfig) {
|
|
54
68
|
this.storageManager = storageManager;
|
|
55
69
|
this.config = config;
|
|
56
|
-
// Auto-detect forwarding mode: tun if root, socket otherwise
|
|
57
|
-
this._forwardingMode = config.forwardingMode
|
|
58
|
-
?? (process.getuid?.() === 0 ? 'tun' : 'socket');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** The effective forwarding mode (tun or socket). */
|
|
62
|
-
public get forwardingMode(): 'tun' | 'socket' {
|
|
63
|
-
return this._forwardingMode;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
/** The VPN subnet CIDR. */
|
|
@@ -92,7 +98,7 @@ export class VpnManager {
|
|
|
92
98
|
publicKey: client.noisePublicKey,
|
|
93
99
|
wgPublicKey: client.wgPublicKey,
|
|
94
100
|
enabled: client.enabled,
|
|
95
|
-
|
|
101
|
+
serverDefinedClientTags: client.serverDefinedClientTags,
|
|
96
102
|
description: client.description,
|
|
97
103
|
assignedIp: client.assignedIp,
|
|
98
104
|
expiresAt: client.expiresAt,
|
|
@@ -113,16 +119,33 @@ export class VpnManager {
|
|
|
113
119
|
publicKey: this.serverKeys.noisePublicKey,
|
|
114
120
|
subnet,
|
|
115
121
|
dns: this.config.dns,
|
|
116
|
-
forwardingMode:
|
|
122
|
+
forwardingMode: 'socket',
|
|
117
123
|
transportMode: 'all',
|
|
118
124
|
wgPrivateKey: this.serverKeys.wgPrivateKey,
|
|
119
125
|
wgListenPort,
|
|
120
126
|
clients: clientEntries,
|
|
121
|
-
socketForwardProxyProtocol:
|
|
127
|
+
socketForwardProxyProtocol: true,
|
|
128
|
+
destinationPolicy: this.config.destinationPolicy
|
|
129
|
+
?? { default: 'forceTarget' as const, target: '127.0.0.1' },
|
|
122
130
|
};
|
|
123
131
|
|
|
124
132
|
await this.vpnServer.start(serverConfig);
|
|
125
|
-
|
|
133
|
+
|
|
134
|
+
// Create initial clients from config (idempotent — skip already-persisted)
|
|
135
|
+
if (this.config.initialClients) {
|
|
136
|
+
for (const initial of this.config.initialClients) {
|
|
137
|
+
if (!this.clients.has(initial.clientId)) {
|
|
138
|
+
const bundle = await this.createClient({
|
|
139
|
+
clientId: initial.clientId,
|
|
140
|
+
serverDefinedClientTags: initial.serverDefinedClientTags,
|
|
141
|
+
description: initial.description,
|
|
142
|
+
});
|
|
143
|
+
logger.log('info', `VPN: Created initial client '${initial.clientId}' (IP: ${bundle.entry.assignedIp})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
logger.log('info', `VPN server started: subnet=${subnet}, wg=:${wgListenPort}, clients=${this.clients.size}`);
|
|
126
149
|
}
|
|
127
150
|
|
|
128
151
|
/**
|
|
@@ -148,7 +171,7 @@ export class VpnManager {
|
|
|
148
171
|
*/
|
|
149
172
|
public async createClient(opts: {
|
|
150
173
|
clientId: string;
|
|
151
|
-
|
|
174
|
+
serverDefinedClientTags?: string[];
|
|
152
175
|
description?: string;
|
|
153
176
|
}): Promise<plugins.smartvpn.IClientConfigBundle> {
|
|
154
177
|
if (!this.vpnServer) {
|
|
@@ -157,7 +180,7 @@ export class VpnManager {
|
|
|
157
180
|
|
|
158
181
|
const bundle = await this.vpnServer.createClient({
|
|
159
182
|
clientId: opts.clientId,
|
|
160
|
-
|
|
183
|
+
serverDefinedClientTags: opts.serverDefinedClientTags,
|
|
161
184
|
description: opts.description,
|
|
162
185
|
});
|
|
163
186
|
|
|
@@ -174,7 +197,7 @@ export class VpnManager {
|
|
|
174
197
|
const persisted: IPersistedClient = {
|
|
175
198
|
clientId: bundle.entry.clientId,
|
|
176
199
|
enabled: bundle.entry.enabled ?? true,
|
|
177
|
-
|
|
200
|
+
serverDefinedClientTags: bundle.entry.serverDefinedClientTags,
|
|
178
201
|
description: bundle.entry.description,
|
|
179
202
|
assignedIp: bundle.entry.assignedIp,
|
|
180
203
|
noisePublicKey: bundle.entry.publicKey,
|
|
@@ -186,6 +209,7 @@ export class VpnManager {
|
|
|
186
209
|
this.clients.set(persisted.clientId, persisted);
|
|
187
210
|
await this.persistClient(persisted);
|
|
188
211
|
|
|
212
|
+
this.config.onClientChanged?.();
|
|
189
213
|
return bundle;
|
|
190
214
|
}
|
|
191
215
|
|
|
@@ -199,6 +223,7 @@ export class VpnManager {
|
|
|
199
223
|
await this.vpnServer.removeClient(clientId);
|
|
200
224
|
this.clients.delete(clientId);
|
|
201
225
|
await this.storageManager.delete(`${STORAGE_PREFIX_CLIENTS}${clientId}`);
|
|
226
|
+
this.config.onClientChanged?.();
|
|
202
227
|
}
|
|
203
228
|
|
|
204
229
|
/**
|
|
@@ -220,6 +245,7 @@ export class VpnManager {
|
|
|
220
245
|
client.updatedAt = Date.now();
|
|
221
246
|
await this.persistClient(client);
|
|
222
247
|
}
|
|
248
|
+
this.config.onClientChanged?.();
|
|
223
249
|
}
|
|
224
250
|
|
|
225
251
|
/**
|
|
@@ -234,6 +260,7 @@ export class VpnManager {
|
|
|
234
260
|
client.updatedAt = Date.now();
|
|
235
261
|
await this.persistClient(client);
|
|
236
262
|
}
|
|
263
|
+
this.config.onClientChanged?.();
|
|
237
264
|
}
|
|
238
265
|
|
|
239
266
|
/**
|
|
@@ -283,6 +310,22 @@ export class VpnManager {
|
|
|
283
310
|
return config;
|
|
284
311
|
}
|
|
285
312
|
|
|
313
|
+
// ── Tag-based access control ───────────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get assigned IPs for all enabled clients matching any of the given server-defined tags.
|
|
317
|
+
*/
|
|
318
|
+
public getClientIpsForServerDefinedTags(tags: string[]): string[] {
|
|
319
|
+
const ips: string[] = [];
|
|
320
|
+
for (const client of this.clients.values()) {
|
|
321
|
+
if (!client.enabled || !client.assignedIp) continue;
|
|
322
|
+
if (client.serverDefinedClientTags?.some(t => tags.includes(t))) {
|
|
323
|
+
ips.push(client.assignedIp);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return ips;
|
|
327
|
+
}
|
|
328
|
+
|
|
286
329
|
// ── Status and telemetry ───────────────────────────────────────────────
|
|
287
330
|
|
|
288
331
|
/**
|
|
@@ -364,6 +407,12 @@ export class VpnManager {
|
|
|
364
407
|
for (const key of keys) {
|
|
365
408
|
const client = await this.storageManager.getJSON<IPersistedClient>(key);
|
|
366
409
|
if (client) {
|
|
410
|
+
// Migrate legacy `tags` → `serverDefinedClientTags`
|
|
411
|
+
if (!client.serverDefinedClientTags && client.tags) {
|
|
412
|
+
client.serverDefinedClientTags = client.tags;
|
|
413
|
+
delete client.tags;
|
|
414
|
+
await this.persistClient(client);
|
|
415
|
+
}
|
|
367
416
|
this.clients.set(client.clientId, client);
|
|
368
417
|
}
|
|
369
418
|
}
|
package/ts_web/appstate.ts
CHANGED
|
@@ -974,7 +974,7 @@ export const fetchVpnAction = vpnStatePart.createAction(async (statePartArg): Pr
|
|
|
974
974
|
|
|
975
975
|
export const createVpnClientAction = vpnStatePart.createAction<{
|
|
976
976
|
clientId: string;
|
|
977
|
-
|
|
977
|
+
serverDefinedClientTags?: string[];
|
|
978
978
|
description?: string;
|
|
979
979
|
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
980
980
|
const context = getActionContext();
|
|
@@ -988,7 +988,7 @@ export const createVpnClientAction = vpnStatePart.createAction<{
|
|
|
988
988
|
const response = await request.fire({
|
|
989
989
|
identity: context.identity!,
|
|
990
990
|
clientId: dataArg.clientId,
|
|
991
|
-
|
|
991
|
+
serverDefinedClientTags: dataArg.serverDefinedClientTags,
|
|
992
992
|
description: dataArg.description,
|
|
993
993
|
});
|
|
994
994
|
|
|
@@ -181,7 +181,7 @@ export class OpsViewVpn extends DeesElement {
|
|
|
181
181
|
type: 'text',
|
|
182
182
|
value: status?.running ? 'Running' : 'Stopped',
|
|
183
183
|
icon: 'lucide:server',
|
|
184
|
-
description: status?.running ?
|
|
184
|
+
description: status?.running ? 'Active' : 'VPN server not running',
|
|
185
185
|
color: status?.running ? '#10b981' : '#ef4444',
|
|
186
186
|
},
|
|
187
187
|
];
|
|
@@ -232,10 +232,6 @@ export class OpsViewVpn extends DeesElement {
|
|
|
232
232
|
<span class="infoLabel">WireGuard Port</span>
|
|
233
233
|
<span class="infoValue">${status.wgListenPort}</span>
|
|
234
234
|
</div>
|
|
235
|
-
<div class="infoItem">
|
|
236
|
-
<span class="infoLabel">Forwarding Mode</span>
|
|
237
|
-
<span class="infoValue">${status.forwardingMode}</span>
|
|
238
|
-
</div>
|
|
239
235
|
${status.serverPublicKeys ? html`
|
|
240
236
|
<div class="infoItem">
|
|
241
237
|
<span class="infoLabel">WG Public Key</span>
|
|
@@ -255,8 +251,8 @@ export class OpsViewVpn extends DeesElement {
|
|
|
255
251
|
? html`<span class="statusBadge enabled">enabled</span>`
|
|
256
252
|
: html`<span class="statusBadge disabled">disabled</span>`,
|
|
257
253
|
'VPN IP': client.assignedIp || '-',
|
|
258
|
-
'Tags': client.
|
|
259
|
-
? html`${client.
|
|
254
|
+
'Tags': client.serverDefinedClientTags?.length
|
|
255
|
+
? html`${client.serverDefinedClientTags.map(t => html`<span class="tagBadge">${t}</span>`)}`
|
|
260
256
|
: '-',
|
|
261
257
|
'Description': client.description || '-',
|
|
262
258
|
'Created': new Date(client.createdAt).toLocaleDateString(),
|
|
@@ -312,11 +308,11 @@ export class OpsViewVpn extends DeesElement {
|
|
|
312
308
|
action: async (modal: any) => {
|
|
313
309
|
const form = modal.shadowRoot!.querySelector('dees-form') as any;
|
|
314
310
|
const data = await form.collectFormData();
|
|
315
|
-
const
|
|
311
|
+
const serverDefinedClientTags = data.tags ? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean) : undefined;
|
|
316
312
|
await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
|
|
317
313
|
clientId: data.clientId,
|
|
318
314
|
description: data.description || undefined,
|
|
319
|
-
|
|
315
|
+
serverDefinedClientTags,
|
|
320
316
|
});
|
|
321
317
|
modal.destroy();
|
|
322
318
|
},
|