@serve.zone/dcrouter 17.8.0 → 17.9.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/deno.json +1 -1
- package/dist_serve/bundle.js +373 -370
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/config/classes.db-seeder.js +9 -1
- package/dist_ts/config/helpers.http-redirects.js +28 -1
- package/dist_ts/config/helpers.special-forwards.js +12 -9
- package/dist_ts/email/classes.email-route-builder.js +5 -1
- package/dist_ts/opsserver/handlers/gatewayclient.handler.js +23 -2
- package/dist_ts/remoteingress/edge-derivation-routes.js +5 -1
- package/dist_ts/security/classes.route-policy-augmenter.d.ts +10 -6
- package/dist_ts/security/classes.route-policy-augmenter.js +65 -51
- package/dist_ts_interfaces/data/remoteingress.d.ts +14 -2
- package/dist_ts_migrations/index.js +66 -2
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/network/ops-view-routes.js +41 -1
- package/dist_ts_web/elements/network/special-forward-dialog.js +2 -2
- package/package.json +4 -4
- package/readme.plan.md +22 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/config/classes.db-seeder.ts +8 -0
- package/ts/config/helpers.http-redirects.ts +31 -1
- package/ts/config/helpers.special-forwards.ts +11 -8
- package/ts/email/classes.email-route-builder.ts +4 -0
- package/ts/opsserver/handlers/gatewayclient.handler.ts +24 -1
- package/ts/remoteingress/edge-derivation-routes.ts +4 -0
- package/ts/security/classes.route-policy-augmenter.ts +70 -57
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/network/ops-view-routes.ts +38 -0
- package/ts_web/elements/network/special-forward-dialog.ts +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
/**
|
|
3
3
|
* Pure route-policy transformations applied before routes reach SmartProxy:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* route-level trusted ingress authorization, listener-level inbound PROXY
|
|
5
|
+
* protocol policy, and merging of compiled security policies.
|
|
6
6
|
*/
|
|
7
7
|
export class RoutePolicyAugmenter {
|
|
8
8
|
dcRouterRef;
|
|
@@ -10,42 +10,53 @@ export class RoutePolicyAugmenter {
|
|
|
10
10
|
this.dcRouterRef = dcRouterRef;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Compile each dcrouter ingress policy into SmartProxy route security, then
|
|
14
|
+
* derive one compatible PROXY protocol mode for every shared listener.
|
|
15
|
+
*
|
|
16
|
+
* The listener mode only controls parsing. Route security remains the
|
|
17
|
+
* authorization boundary, so a direct request cannot fall through to a
|
|
18
|
+
* wildcard route that only permits RemoteIngress or SmartVPN.
|
|
15
19
|
*/
|
|
16
20
|
applyInboundProxyProtocolPolicies(routes) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
continue;
|
|
21
|
+
const routesWithIngressSecurity = routes.map((route) => {
|
|
22
|
+
const allowedOrigins = this.getAllowedIngressOrigins(route);
|
|
23
|
+
if (allowedOrigins.length === 0) {
|
|
24
|
+
throw new Error(`Route '${route.name}' has no enabled ingress path`);
|
|
22
25
|
}
|
|
26
|
+
return {
|
|
27
|
+
...route,
|
|
28
|
+
security: {
|
|
29
|
+
...route.security,
|
|
30
|
+
ingress: {
|
|
31
|
+
allowedOrigins,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
const originsByListener = new Map();
|
|
37
|
+
for (const route of routesWithIngressSecurity) {
|
|
38
|
+
const origins = route.security.ingress.allowedOrigins;
|
|
23
39
|
for (const listenerKey of this.getInboundProxyListenerKeys(route)) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
const listenerOrigins = originsByListener.get(listenerKey) || new Set();
|
|
41
|
+
for (const origin of origins) {
|
|
42
|
+
listenerOrigins.add(origin);
|
|
27
43
|
}
|
|
44
|
+
originsByListener.set(listenerKey, listenerOrigins);
|
|
28
45
|
}
|
|
29
46
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return routes.map((route) => {
|
|
34
|
-
if (route.match?.inboundProxyProtocol) {
|
|
35
|
-
return route;
|
|
36
|
-
}
|
|
37
|
-
let listenerPolicy;
|
|
47
|
+
return routesWithIngressSecurity.map((route) => {
|
|
48
|
+
const listenerOrigins = new Set();
|
|
38
49
|
for (const listenerKey of this.getInboundProxyListenerKeys(route)) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return route;
|
|
50
|
+
for (const origin of originsByListener.get(listenerKey) || []) {
|
|
51
|
+
listenerOrigins.add(origin);
|
|
52
|
+
}
|
|
43
53
|
}
|
|
54
|
+
const inboundProxyProtocol = this.getListenerProxyProtocolPolicy(listenerOrigins);
|
|
44
55
|
return {
|
|
45
56
|
...route,
|
|
46
57
|
match: {
|
|
47
58
|
...route.match,
|
|
48
|
-
inboundProxyProtocol
|
|
59
|
+
inboundProxyProtocol,
|
|
49
60
|
},
|
|
50
61
|
};
|
|
51
62
|
});
|
|
@@ -72,19 +83,37 @@ export class RoutePolicyAugmenter {
|
|
|
72
83
|
blockedCidrs: [...blockedCidrs].sort(),
|
|
73
84
|
};
|
|
74
85
|
}
|
|
75
|
-
|
|
86
|
+
getAllowedIngressOrigins(route) {
|
|
76
87
|
const dcRoute = route;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
const origins = new Set();
|
|
89
|
+
if (dcRoute.ingress) {
|
|
90
|
+
if (dcRoute.ingress.directHub)
|
|
91
|
+
origins.add('direct');
|
|
92
|
+
if (dcRoute.ingress.smartVpn)
|
|
93
|
+
origins.add('smartVpn');
|
|
83
94
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
95
|
+
else if (dcRoute.vpnOnly) {
|
|
96
|
+
origins.add('smartVpn');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Compatibility for runtime routes and producers predating the explicit
|
|
100
|
+
// contract. Persisted routes are backfilled by the versioned migration.
|
|
101
|
+
origins.add('direct');
|
|
102
|
+
origins.add('smartVpn');
|
|
86
103
|
}
|
|
87
|
-
|
|
104
|
+
if (dcRoute.remoteIngress?.enabled) {
|
|
105
|
+
origins.add('trustedProxy');
|
|
106
|
+
}
|
|
107
|
+
return [...origins];
|
|
108
|
+
}
|
|
109
|
+
getListenerProxyProtocolPolicy(origins) {
|
|
110
|
+
const allowsDirect = origins.has('direct');
|
|
111
|
+
const allowsProxied = origins.has('trustedProxy') || origins.has('smartVpn');
|
|
112
|
+
if (allowsDirect && allowsProxied)
|
|
113
|
+
return { mode: 'optional' };
|
|
114
|
+
if (allowsProxied)
|
|
115
|
+
return { mode: 'required' };
|
|
116
|
+
return { mode: 'reject' };
|
|
88
117
|
}
|
|
89
118
|
getInboundProxyListenerKeys(route) {
|
|
90
119
|
const ports = plugins.smartproxy.expandPortRange(route.match.ports);
|
|
@@ -101,20 +130,5 @@ export class RoutePolicyAugmenter {
|
|
|
101
130
|
}
|
|
102
131
|
return keys;
|
|
103
132
|
}
|
|
104
|
-
mergeInboundProxyProtocolPolicies(current, next) {
|
|
105
|
-
if (!current)
|
|
106
|
-
return next;
|
|
107
|
-
if (!next)
|
|
108
|
-
return current;
|
|
109
|
-
if (current.mode === 'required')
|
|
110
|
-
return current;
|
|
111
|
-
if (next.mode === 'required')
|
|
112
|
-
return next;
|
|
113
|
-
if (current.mode === 'optional')
|
|
114
|
-
return current;
|
|
115
|
-
if (next.mode === 'optional')
|
|
116
|
-
return next;
|
|
117
|
-
return current;
|
|
118
|
-
}
|
|
119
133
|
}
|
|
120
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
134
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yb3V0ZS1wb2xpY3ktYXVnbWVudGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvc2VjdXJpdHkvY2xhc3Nlcy5yb3V0ZS1wb2xpY3ktYXVnbWVudGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBVXpDOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sb0JBQW9CO0lBQ1g7SUFBcEIsWUFBb0IsV0FBcUI7UUFBckIsZ0JBQVcsR0FBWCxXQUFXLENBQVU7SUFBRyxDQUFDO0lBRTdDOzs7Ozs7O09BT0c7SUFDSSxpQ0FBaUMsQ0FDdEMsTUFBeUM7UUFFekMsTUFBTSx5QkFBeUIsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDckQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzVELElBQUksY0FBYyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztnQkFDaEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxVQUFVLEtBQUssQ0FBQyxJQUFJLCtCQUErQixDQUFDLENBQUM7WUFDdkUsQ0FBQztZQUNELE9BQU87Z0JBQ0wsR0FBRyxLQUFLO2dCQUNSLFFBQVEsRUFBRTtvQkFDUixHQUFHLEtBQUssQ0FBQyxRQUFRO29CQUNqQixPQUFPLEVBQUU7d0JBQ1AsY0FBYztxQkFDZjtpQkFDRjthQUNGLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztRQUVILE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQStCLENBQUM7UUFDakUsS0FBSyxNQUFNLEtBQUssSUFBSSx5QkFBeUIsRUFBRSxDQUFDO1lBQzlDLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxRQUFTLENBQUMsT0FBUSxDQUFDLGNBQWMsQ0FBQztZQUN4RCxLQUFLLE1BQU0sV0FBVyxJQUFJLElBQUksQ0FBQywy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
|
|
@@ -148,14 +148,26 @@ export interface IRouteRemoteIngress {
|
|
|
148
148
|
* When absent, the route applies to all edges. */
|
|
149
149
|
edgeFilter?: string[];
|
|
150
150
|
}
|
|
151
|
+
export interface IRouteIngress {
|
|
152
|
+
/** Accept traffic connected directly to the gateway hub listener. */
|
|
153
|
+
directHub: boolean;
|
|
154
|
+
/** Accept authenticated SmartVPN traffic carrying trusted VPN metadata. */
|
|
155
|
+
smartVpn: boolean;
|
|
156
|
+
}
|
|
151
157
|
/**
|
|
152
158
|
* Extended route config used within dcrouter.
|
|
153
|
-
* Adds
|
|
159
|
+
* Adds explicit ingress path policy and dcrouter compatibility properties to
|
|
160
|
+
* SmartProxy's IRouteConfig.
|
|
154
161
|
* SmartProxy ignores unknown properties at runtime.
|
|
155
162
|
*/
|
|
156
163
|
export type IDcRouterRouteConfig = IRouteConfig & {
|
|
164
|
+
/**
|
|
165
|
+
* Explicit trusted ingress paths. New routes must opt in; direct hub
|
|
166
|
+
* listening is intentionally not a default.
|
|
167
|
+
*/
|
|
168
|
+
ingress?: IRouteIngress;
|
|
157
169
|
remoteIngress?: IRouteRemoteIngress;
|
|
158
|
-
/**
|
|
170
|
+
/** Compatibility policy: only VPN clients whose TargetProfile matches this route get access.
|
|
159
171
|
* Matching is determined by domain overlap, target overlap, or direct routeRef. */
|
|
160
172
|
vpnOnly?: boolean;
|
|
161
173
|
};
|