Initial Commit
This commit is contained in:
commit
c3bc1d993e
54
.devcontainer/devcontainer.json
Normal file
54
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
// Displayed name
|
||||
"name": "suricata-eve-logger",
|
||||
|
||||
"build": {
|
||||
"dockerfile": "../DOCKERFILE"
|
||||
},
|
||||
|
||||
// Env in container
|
||||
"containerEnv": {
|
||||
|
||||
},
|
||||
|
||||
// Customization
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"denoland.vscode-deno",
|
||||
"fabiospampinato.vscode-highlight",
|
||||
"fabiospampinato.vscode-todo-plus"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Mounts in container
|
||||
"mounts": [
|
||||
{
|
||||
"source": "${localWorkspaceFolder}",
|
||||
"target": "/workspace",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"source": "${localWorkspaceFolder}/suricata/suricata.rules",
|
||||
"target": "/var/lib/suricata/rules/suricata.rules",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"source": "${localWorkspaceFolder}/suricata/suricata.yaml",
|
||||
"target": "/etc/suricata/suricata.yaml",
|
||||
"type": "bind"
|
||||
}
|
||||
],
|
||||
|
||||
// The WorkspaceFolder inside container
|
||||
"workspaceFolder": "/workspace",
|
||||
|
||||
// RunArgs
|
||||
"runArgs": [
|
||||
"--name",
|
||||
"suricata-logger"
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
deno.lock
|
||||
http/testdata/%25A.txt
|
||||
http/testdata/file#2.txt
|
||||
http/testdata/test file.txt
|
||||
docs/
|
||||
|
||||
# rust build output
|
||||
crypto/_wasm/target
|
||||
cli/testdata/unicode_width_crate/target
|
||||
|
||||
# coverage
|
||||
coverage/
|
||||
|
||||
# misc files
|
||||
.DS_Store
|
||||
.idea
|
||||
.vim
|
||||
_tmp/
|
||||
!_tmp/.keep
|
||||
|
||||
# tsconfig for bun
|
||||
/tsconfig.json
|
||||
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"type": "node",
|
||||
"program": "${workspaceFolder}/src/main.ts",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {},
|
||||
"runtimeExecutable": "/usr/bin/deno",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"--unstable",
|
||||
"--inspect-wait",
|
||||
"--allow-all"
|
||||
],
|
||||
"attachSimplePort": 9229,
|
||||
"preLaunchTask": "cleanup",
|
||||
"console": "integratedTerminal"
|
||||
// "postDebugTask": "cleanup"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"deno.enable": true
|
||||
}
|
||||
64
.vscode/tasks.json
vendored
Normal file
64
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "stop-suricata",
|
||||
"type": "shell",
|
||||
"command": "systemctl",
|
||||
"args": [
|
||||
"stop",
|
||||
"suricata"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "start-suricata",
|
||||
"type": "shell",
|
||||
"command": "systemctl",
|
||||
"args": [
|
||||
"start",
|
||||
"suricata"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "remove-socket",
|
||||
"type": "shell",
|
||||
"command": "rm",
|
||||
"args": [
|
||||
"-rf",
|
||||
"/var/lib/suricata/eve.sock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "clean-output",
|
||||
"type": "shell",
|
||||
"command": "rm",
|
||||
"args": [
|
||||
"-rf",
|
||||
"${workspaceFolder}/output/parsed.log",
|
||||
"${workspaceFolder}/output/raw.log",
|
||||
"${workspaceFolder}/output/semi-parsed.log"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "touch-output",
|
||||
"type": "shell",
|
||||
"command": "touch",
|
||||
"args": [
|
||||
"${workspaceFolder}/output/parsed.log",
|
||||
"${workspaceFolder}/output/raw.log",
|
||||
"${workspaceFolder}/output/semi-parsed.log"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "cleanup",
|
||||
"type": "shell",
|
||||
"command": "echo cleanup done",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"remove-socket",
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
5
DOCKERFILE
Normal file
5
DOCKERFILE
Normal file
@ -0,0 +1,5 @@
|
||||
FROM denoland/deno
|
||||
|
||||
RUN apt update && apt upgrade -y
|
||||
RUN apt install -y git suricata nano
|
||||
RUN apt install -y systemctl curl iproute2 ethtool
|
||||
8
deno.json
Normal file
8
deno.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --watch main.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@std/assert": "jsr:@std/assert@1"
|
||||
}
|
||||
}
|
||||
12
src/main.ts
Normal file
12
src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { SuricataSocketStream } from "./socket-server/server.ts";
|
||||
|
||||
// UGLY: take them from
|
||||
const UNIXPATH = "/var/lib/suricata/eve.sock"
|
||||
const WEB_SOCKET_URI = ""
|
||||
|
||||
const app = new SuricataSocketStream(
|
||||
UNIXPATH,
|
||||
WEB_SOCKET_URI
|
||||
)
|
||||
|
||||
app.serve()
|
||||
125
src/socket-server/json_chunker.ts
Normal file
125
src/socket-server/json_chunker.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { JSON_BytesToken } from "../utils/json_byte_repr.ts";
|
||||
|
||||
|
||||
// TODO: make this an iterator
|
||||
export class JSON_Chunker {
|
||||
private jsons: Array<string> = [];
|
||||
private chunks: Array<Uint8Array> = [];
|
||||
private totalBytes = 0;
|
||||
private _openBrackets = 0;
|
||||
private _openArray = 0;
|
||||
private decoder = new TextDecoder("utf-8");
|
||||
private debug = true;
|
||||
|
||||
private get delimiterToken() {
|
||||
return this.openArray + this.openBrackets
|
||||
}
|
||||
|
||||
private get openBrackets() {
|
||||
return this._openBrackets;
|
||||
}
|
||||
|
||||
private set openBrackets(value) {
|
||||
this._openBrackets = value;
|
||||
|
||||
if (this._openBrackets < 0) {
|
||||
this._openBrackets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private get openArray() {
|
||||
return this._openArray;
|
||||
}
|
||||
|
||||
private set openArray(value) {
|
||||
this._openArray = value;
|
||||
|
||||
if (this._openArray < 0) {
|
||||
this._openArray = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public get messageReady() {
|
||||
return (this.jsons.length !== 0);
|
||||
}
|
||||
|
||||
public get message() {
|
||||
const msg = this.jsons.shift();
|
||||
return msg;
|
||||
}
|
||||
|
||||
public pushChunk(chunk: Uint8Array) {
|
||||
|
||||
const chunkLen = chunk.length;
|
||||
let lastByteRead = 0;
|
||||
|
||||
for (let i = 0; i < chunkLen; i++) {
|
||||
|
||||
const byte = chunk[i];
|
||||
|
||||
switch (byte) {
|
||||
case JSON_BytesToken.OPEN_ARR:
|
||||
this.openArray = this.openArray + 1;
|
||||
break;
|
||||
case JSON_BytesToken.CLOSE_ARR:
|
||||
this.openArray = this.openArray - 1;
|
||||
break;
|
||||
case JSON_BytesToken.OPEN_BRACKET:
|
||||
this.openBrackets = this.openBrackets + 1;
|
||||
break;
|
||||
case JSON_BytesToken.CLOSE_BRACKET:
|
||||
this.openBrackets = this.openBrackets - 1;
|
||||
break;
|
||||
default:
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are in this situation, then we have a JSON
|
||||
if (this.delimiterToken === 0) {
|
||||
const _chunk = chunk.slice(lastByteRead, i + 1);
|
||||
|
||||
// console.log(this.decoder.decode(_chunk));
|
||||
this.insertChunk(_chunk);
|
||||
this.flush();
|
||||
lastByteRead = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise insert the remaining chunk
|
||||
this.insertChunk(
|
||||
chunk.slice(lastByteRead),
|
||||
);
|
||||
}
|
||||
|
||||
private insertChunk(chunk: Uint8Array) {
|
||||
this.totalBytes += chunk.length;
|
||||
this.chunks.push(chunk);
|
||||
}
|
||||
|
||||
private flush() {
|
||||
let remainingBytes = this.totalBytes;
|
||||
let writtenBytes = 0;
|
||||
|
||||
const msg = new Uint8Array(this.totalBytes);
|
||||
|
||||
for (let chunk of this.chunks) {
|
||||
const validBytes = Math.min(
|
||||
remainingBytes,
|
||||
chunk.length,
|
||||
);
|
||||
chunk = chunk.slice(0, validBytes);
|
||||
msg.set(chunk, writtenBytes);
|
||||
|
||||
writtenBytes += validBytes;
|
||||
remainingBytes -= validBytes;
|
||||
}
|
||||
|
||||
|
||||
this.totalBytes = 0;
|
||||
this.chunks = []
|
||||
|
||||
const message = this.decoder.decode(msg);
|
||||
this.jsons.push(message);
|
||||
}
|
||||
}
|
||||
75
src/socket-server/server.ts
Normal file
75
src/socket-server/server.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { delay } from "../utils/delay.ts";
|
||||
import { JSON_Chunker } from "./json_chunker.ts";
|
||||
|
||||
export class SuricataSocketStream {
|
||||
private listener: Deno.UnixListener;
|
||||
private maxBlobSize: number = 1024;
|
||||
private decoder: TextDecoder = new TextDecoder("utf-8");
|
||||
private webSocket: WebSocket;
|
||||
|
||||
constructor(
|
||||
unixSocketPath: string,
|
||||
webSocketURI: string,
|
||||
) {
|
||||
this.listener = Deno.listen({
|
||||
path: unixSocketPath,
|
||||
transport: "unix",
|
||||
});
|
||||
|
||||
this.webSocket = new WebSocket(webSocketURI);
|
||||
|
||||
}
|
||||
|
||||
public async serve() {
|
||||
// Await for connections
|
||||
for await (const conn of this.listener) {
|
||||
|
||||
while (this.webSocket.readyState !== this.webSocket.OPEN) {
|
||||
await delay(20);
|
||||
}
|
||||
|
||||
const jsonParser = new JSON_Chunker()
|
||||
|
||||
do {
|
||||
|
||||
let bytesRead: number | null = 0;
|
||||
const chunk: Uint8Array = new Uint8Array(this.maxBlobSize);
|
||||
|
||||
bytesRead = await conn.read(chunk);
|
||||
|
||||
// Connection has ended,
|
||||
// break the cycle
|
||||
if (!bytesRead) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
jsonParser.pushChunk(chunk.slice(0, bytesRead))
|
||||
|
||||
while (jsonParser.messageReady) {
|
||||
this.messageProcess(jsonParser.message!)
|
||||
}
|
||||
|
||||
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UGLY: change this to a more flexible method
|
||||
private messageProcess(msg: string | Uint8Array) {
|
||||
// this.webSocket.send(msg);
|
||||
console.log(
|
||||
`
|
||||
Debug, found this message:
|
||||
|
||||
${msg}
|
||||
|
||||
END OF MESSAGE
|
||||
|
||||
|
||||
`
|
||||
)
|
||||
}
|
||||
}
|
||||
9
src/utils/delay.ts
Normal file
9
src/utils/delay.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export function delay(ms: number) {
|
||||
return new Promise(
|
||||
(resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve
|
||||
}, ms)
|
||||
}
|
||||
)
|
||||
}
|
||||
6
src/utils/json_byte_repr.ts
Normal file
6
src/utils/json_byte_repr.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum JSON_BytesToken{
|
||||
OPEN_ARR = 0x5B,
|
||||
CLOSE_ARR = 0x5D,
|
||||
OPEN_BRACKET = 0x7B,
|
||||
CLOSE_BRACKET = 0x7D
|
||||
}
|
||||
1
suricata/suricata.rules
Normal file
1
suricata/suricata.rules
Normal file
@ -0,0 +1 @@
|
||||
alert ip any any -> any any (msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:2100498; rev:7; metadata:created_at 2010_09_23, updated_at 2010_09_23;)
|
||||
1943
suricata/suricata.yaml
Normal file
1943
suricata/suricata.yaml
Normal file
File diff suppressed because it is too large
Load Diff
0
tests/main_test.ts
Normal file
0
tests/main_test.ts
Normal file
Loading…
x
Reference in New Issue
Block a user