Add packaged DLP broker and protected Windows service foundation

This commit is contained in:
Karl committed 2026-10-14 14:19:22 +02:00
1 parent 95d23f4e0b
commit 7b5057901a
140 files changed
+11215 -60

No files matched your search

@@ -0,0 +1,62 @@
'use strict';
const path=require('node:path');
const {createDnsPolicyRoute,assertSupportedDns}=require('./dns-policy-route.cjs');
const tls=require('node:tls');
const {X509Certificate}=require('node:crypto');
const {z}=require('zod');
const {createPolicyAuthority}=require('./policy-authority.cjs');
const {createManagedProxy}=require('./managed-proxy.cjs');
const {createSessionCertificates}=require('./tls-certificates.cjs');
const {createProxyForwarder,parseFixedProxy}=require('./proxy-forwarder.cjs');
const {createHttpsForwarder}=require('./https-forwarder.cjs');
const {createDownloadResponseGate}=require('./download-response-gate.cjs');
const {startInspector}=require('../browser/inspection.cjs');
const {scanEnterprise}=require('../browser/enterprise-inspection.cjs');
const networkBase={dns:z.literal('system'),ca:z.string().min(1).max(131072).optional()};
const NetworkSchema=z.discriminatedUnion('mode',[z.object({mode:z.literal('direct'),...networkBase}).strict(),z.object({mode:z.literal('fixed_servers'),...networkBase,proxyCa:z.string().min(1).max(131072).optional(),credentials:z.object({username:z.string().min(1).max(1024).refine(value=>!value.includes(':')&&![...value].some(character=>character.charCodeAt(0)<32||character.charCodeAt(0)===127)),password:z.string().min(1).max(3072).refine(value=>![...value].some(character=>character.charCodeAt(0)<32||character.charCodeAt(0)===127))}).strict().optional()}).strict()]);
const BootstrapSchema=z.object({publicKey:z.string().min(1).max(16384),deviceId:z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/),minimumVersion:z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),envelope:z.object({payload:z.string().min(1).max(2097152),signature:z.string().min(1).max(16384)}).strict(),brokerPort:z.number().int().min(1).max(65535),network:NetworkSchema}).strict();
function deploymentRoots(pem){
if(pem===undefined)return undefined;
const matches=pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
if(!matches||matches.length>32||pem.replace(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g,'').trim())throw Error('broker-bootstrap-invalid');
for(const item of matches){let certificate;try{certificate=new X509Certificate(item);}catch{throw Error('broker-bootstrap-invalid');}if(!certificate.ca||Date.parse(certificate.validFrom)>Date.now()||Date.parse(certificate.validTo)<=Date.now())throw Error('broker-bootstrap-invalid');}
return Object.freeze([...tls.rootCertificates,...matches]);
}
/** Real packaged-resource factory. Parent verifies its signed package before
* spawn; IPC cannot choose modules, resource paths, keys or scanner callbacks.
* Direct or one signed fixed HTTP(S) proxy only; PAC/system/bypass fail closed.
*/
async function createProductionBroker(config,{persistAudit,acknowledgeCertificate,expectedPolicyVersion}={}){
let c;try{c=BootstrapSchema.parse(config);}catch{throw Error('broker-bootstrap-invalid');}
if(typeof persistAudit!=='function'||typeof acknowledgeCertificate!=='function')throw Error('broker-bootstrap-invalid');
const ca=deploymentRoots(c.network.ca),proxyCa=deploymentRoots(c.network.proxyCa);
const brokerRoot=path.resolve(__dirname,'../..'),scannerResourcesRoot=path.dirname(brokerRoot);
const authority=await createPolicyAuthority({publicKey:c.publicKey,deviceId:c.deviceId,minimumVersion:c.minimumVersion});
const snapshot=authority.accept(c.envelope),policy=snapshot.policy;
if(expectedPolicyVersion!==policy.version)throw Error('broker-bootstrap-invalid');
if(policy.proxy.mode!==c.network.mode)throw Error('broker-route-unsupported');
assertSupportedDns(policy,c.network,{numericOriginSupported:true});
if(c.network.mode==='fixed_servers'){try{parseFixedProxy(policy.proxy);}catch{throw Error('broker-route-unsupported');}}
const core=await import('../../packages/core/policy.mjs'),securityCore=await import('../../packages/core/security.mjs');
const current=token=>authority.current(token),active=()=>current(snapshot.token);
const approved=binding=>active()&&core.destinationAllowed(`https://${binding.authority}/`,policy);
let inspector,issuer,proxy,closed=false,stage='inspector';
const close=async()=>{if(closed)return;closed=true;authority.revoke();try{await proxy?.close();}finally{issuer?.close();inspector?.stop();}};
try{
inspector=startInspector(scannerResourcesRoot,true);if(!inspector)throw Error('broker-inspector-unavailable');await inspector.waitReady();
stage='certificate';issuer=await createSessionCertificates({current:active,authorized:approved});
const maxResponseBytes=Math.min(policy.fileTransfers.download.maxBytes,16777216);
stage='transport';
const resolve=createDnsPolicyRoute({current:active,blocked:address=>securityCore.dnsAddressBlocked(address,policy)});
const transport={authority:snapshot.token,current,ca:ca?.join('\n'),maxRequestBytes:policy.dlp.maxBytes,maxResponseBytes,deadlineMs:30000};
const resolveOriginRoute=async route=>{if(!core.destinationAllowed(`https://${route.hostname.includes(':')?'['+route.hostname+']':route.hostname}:${route.port}/`,policy))throw Error('broker-route-denied');return resolve(route);};
const rawForward=c.network.mode==='fixed_servers'?createProxyForwarder({...transport,proxyPolicy:policy.proxy,proxyCa:proxyCa?.join('\n'),credentials:c.network.credentials,resolveProxyRoute:resolve,resolveOriginRoute:policy.security?.dns?.enabled!==false&&policy.security?.dns?.blockedIPs?.length?resolveOriginRoute:undefined}):createHttpsForwarder({...transport,resolveRoute:resolveOriginRoute});
const forward=createDownloadResponseGate({forward:rawForward,policy,current:active,inspector,maxBytes:maxResponseBytes});
stage='managed';proxy=await createManagedProxy({deadlineMs:30000,policyAuthority:authority,snapshot,persistAudit,localInspector:(bytes,p)=>inspector.inspect(bytes,p),enterpriseInspector:(bytes,p)=>scanEnterprise(bytes,p,'upload'),forward,resolveCertificate:async binding=>{
if(!approved(binding))throw Error('broker-certificate-denied');
const leaf=issuer.resolve(binding);if(await acknowledgeCertificate({authority:leaf.authority,cert:leaf.cert})!==true||!active())throw Error('broker-certificate-denied');return leaf;
}});
return Object.freeze({authMode:proxy.authMode,credentials:proxy.credentials,capability:proxy.capability,listen:()=>proxy.listen(c.brokerPort),close});
}catch{await close();throw Error('broker-startup-'+stage); }
}
module.exports={createProductionBroker,BootstrapSchema,deploymentRoots};