Add packaged DLP broker and protected Windows service foundation
This commit is contained in:
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};
|
||||
Reference in new issue
Block a user