first commit
This commit is contained in:
commit
ff482ebe23
251 files changed
+19126
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { appendFileSync,readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { request } from 'node:https';
|
||||
import { toECS,toCEF } from '../../packages/core/siem.mjs';
|
||||
import { digest } from './store.mjs';
|
||||
export function startSIEM(store,directory,env=process.env) {
|
||||
let stopped=false,running=false,failures=0,nextAttempt=0;
|
||||
const url=env.SIEM_HTTP_URL?new URL(env.SIEM_HTTP_URL):null;
|
||||
if(url&&(url.protocol!=='https:'||url.username||url.password))throw Error('SIEM push requires an HTTPS URL without embedded credentials');
|
||||
const tlsOptions={...(env.SIEM_CA_FILE?{ca:readFileSync(env.SIEM_CA_FILE)}:{}),...(env.SIEM_CERT_FILE?{cert:readFileSync(env.SIEM_CERT_FILE)}:{}),...(env.SIEM_KEY_FILE?{key:readFileSync(env.SIEM_KEY_FILE)}:{})};
|
||||
const format=env.SIEM_FORMAT||'ecs';if(!['ecs','cef'].includes(format))throw Error('SIEM_FORMAT must be ecs or cef');
|
||||
const cursorKey=url?`siem.remote.${digest(url.href+':'+format)}`:`siem.file.${format}`;
|
||||
async function tick(){
|
||||
if(stopped||running||Date.now()<nextAttempt)return;running=true;
|
||||
try{
|
||||
const after=store.get(cursorKey)||0;
|
||||
const rows=store.db.prepare('SELECT * FROM events WHERE id>? ORDER BY id LIMIT 100').all(after).map(e=>({...e,metadata:JSON.parse(e.metadata)}));
|
||||
if(!rows.length)return;
|
||||
const body=rows.map(e=>format==='ecs'?JSON.stringify(toECS(e,store.get('installationId'))):toCEF(e,store.get('installationId'))).join('\n')+'\n';
|
||||
if(url)await push(url,body,format,env.SIEM_HTTP_TOKEN,tlsOptions);
|
||||
else appendFileSync(join(directory,`security.${format==='ecs'?'ndjson':'cef'}`),body,{mode:0o600,flush:true});
|
||||
if(!stopped)store.put(cursorKey,rows.at(-1).id);failures=0;nextAttempt=0;
|
||||
}catch{failures++;nextAttempt=Date.now()+Math.min(300000,1000*2**Math.min(failures,8));}
|
||||
finally{running=false;}
|
||||
}
|
||||
const timer=setInterval(tick,2000);timer.unref();tick();
|
||||
return {stop:()=>{stopped=true;clearInterval(timer);},flush:tick,status:()=>({configured:!!url,format,failures,cursor:store.get(cursorKey)||0,pending:store.db.prepare('SELECT count(*) AS n FROM events WHERE id>?').get(store.get(cursorKey)||0).n})};
|
||||
}
|
||||
function push(url,body,format,credential,tlsOptions) {
|
||||
return new Promise((resolve,reject)=>{
|
||||
const req=request(url,{method:'POST',...tlsOptions,headers:{'content-type':format==='ecs'?'application/x-ndjson':'text/plain','content-length':Buffer.byteLength(body),...(credential?{authorization:`Bearer ${credential}`}:{})}},res=>{
|
||||
res.resume();res.on('error',reject);res.on('end',()=>res.statusCode>=200&&res.statusCode<300?resolve():reject(Error('SIEM rejected batch')));
|
||||
});req.setTimeout(10000,()=>req.destroy(Error('SIEM timeout')));req.on('error',reject);req.end(body);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user