Initial Commit
This commit is contained in:
14
src/app.d.ts
vendored
Normal file
14
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
12
src/app.html
Normal file
12
src/app.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
0
src/db/.gitkeep
Normal file
0
src/db/.gitkeep
Normal file
7
src/hooks.server.ts
Normal file
7
src/hooks.server.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { ServerInit } from '@sveltejs/kit';
|
||||
|
||||
export const init: ServerInit = async () => {
|
||||
|
||||
// TODO: initialize db
|
||||
|
||||
};
|
||||
119
src/lib/classes/endpoint.ts
Normal file
119
src/lib/classes/endpoint.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { NginxProtocol } from "$lib/enums/protocols"
|
||||
import { isPortAvailable, validatePort } from "$lib/utils/ports-utils"
|
||||
|
||||
// TODO: inherit from a super class
|
||||
|
||||
/**
|
||||
* This class represents an SSL Termination Endpoint.
|
||||
*
|
||||
* While it's possible to create it directly, it is
|
||||
* discouraged in favor of the Factory methods as it does
|
||||
* more checks than this class
|
||||
*/
|
||||
export class SSLTerminationEndpoint {
|
||||
|
||||
public sslPort: number
|
||||
public clearPort: number
|
||||
public servicePort: number
|
||||
public protocol: NginxProtocol
|
||||
public certificateURI: string
|
||||
public privateKeyURI: string
|
||||
|
||||
constructor(
|
||||
sslPort: number,
|
||||
clearPort: number,
|
||||
servicePort: number,
|
||||
protocol: NginxProtocol,
|
||||
certificateURI: string,
|
||||
privateKeyURI: string
|
||||
) {
|
||||
validatePort(sslPort)
|
||||
validatePort(clearPort)
|
||||
validatePort(servicePort)
|
||||
|
||||
// TODO: check for file existance
|
||||
|
||||
|
||||
this.sslPort = sslPort
|
||||
this.clearPort = clearPort
|
||||
this.servicePort = servicePort
|
||||
|
||||
this.protocol = protocol
|
||||
this.certificateURI = certificateURI
|
||||
this.privateKeyURI = privateKeyURI
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SSLTerminationEndpointFactory {
|
||||
|
||||
/**
|
||||
* Creates an SSLEndpoint and chooses ports
|
||||
* automatically
|
||||
*
|
||||
* @param servicePort
|
||||
* @param protocol
|
||||
* @param certificateURI
|
||||
* @param privateKeyURI
|
||||
*/
|
||||
public static async createSSLEndpoint(
|
||||
servicePort: number,
|
||||
protocol: NginxProtocol,
|
||||
certificateURI: string,
|
||||
privateKeyURI: string
|
||||
): Promise<SSLTerminationEndpoint>
|
||||
public static async createSSLEndpoint(
|
||||
servicePort: number,
|
||||
protocol: NginxProtocol,
|
||||
certificateURI: string,
|
||||
privateKeyURI: string,
|
||||
sslPort?: number,
|
||||
clearPort?: number,
|
||||
): Promise<SSLTerminationEndpoint> {
|
||||
|
||||
if (!sslPort) {
|
||||
sslPort = await SSLTerminationEndpointFactory.generatePort()
|
||||
}
|
||||
|
||||
if (!clearPort) {
|
||||
clearPort = await SSLTerminationEndpointFactory.generatePort()
|
||||
}
|
||||
|
||||
return new SSLTerminationEndpoint(
|
||||
sslPort,
|
||||
clearPort,
|
||||
servicePort,
|
||||
protocol,
|
||||
certificateURI,
|
||||
privateKeyURI
|
||||
)
|
||||
}
|
||||
|
||||
private static async generatePort() {
|
||||
|
||||
const MIN_PORT = 1025
|
||||
const MAX_PORT = 65535
|
||||
const MAX_RETRIES = 100
|
||||
|
||||
let counter = 0
|
||||
|
||||
while (counter < MAX_RETRIES) {
|
||||
|
||||
let candidatePort = Math.trunc(
|
||||
Math.random() * (MAX_PORT - MIN_PORT) + MIN_PORT
|
||||
)
|
||||
|
||||
if (await isPortAvailable(candidatePort)) {
|
||||
return candidatePort
|
||||
}
|
||||
|
||||
counter++
|
||||
}
|
||||
|
||||
// UGLY: change this to a more specific error
|
||||
throw Error(`Couldn't find an available port in less than ${MAX_RETRIES}`)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
47
src/lib/db-utils/sqlite.ts
Normal file
47
src/lib/db-utils/sqlite.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { DB_PATH } from "$lib/utils/constants";
|
||||
import { Database, Statement, Statement } from "bun:sqlite";
|
||||
|
||||
export class SSLSnifferDatabase {
|
||||
|
||||
private static initialized = false
|
||||
|
||||
private db: Database
|
||||
|
||||
// UGLY: should be more flexible
|
||||
constructor() {
|
||||
this.db = SSLSnifferDatabase.initDatabase()
|
||||
SSLSnifferDatabase.initialized = true
|
||||
}
|
||||
|
||||
// This needs to be static as it doesn't need the object
|
||||
private static initDatabase() {
|
||||
|
||||
const db = new Database(
|
||||
DB_PATH,
|
||||
{
|
||||
create: true,
|
||||
strict: true
|
||||
}
|
||||
)
|
||||
|
||||
// Improve performance
|
||||
db.exec("PRAGMA journal_mode = WAL;");
|
||||
return db
|
||||
|
||||
}
|
||||
|
||||
private closeDatabase(graceful?: boolean) {
|
||||
if (!graceful) {
|
||||
graceful = true
|
||||
}
|
||||
// Change of variable to make it more readable
|
||||
const forceful = !graceful
|
||||
|
||||
this.db.close(forceful)
|
||||
}
|
||||
|
||||
public executeQuery(query: Statement): any {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
src/lib/enums/protocols.ts
Normal file
6
src/lib/enums/protocols.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum NginxProtocol {
|
||||
HTTP = "http",
|
||||
HTTP2 = "http",
|
||||
QUIC = "http",
|
||||
GRPC = "grpc"
|
||||
}
|
||||
2
src/lib/index.ts
Normal file
2
src/lib/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
export {PKG} from "./utils/constants";
|
||||
17
src/lib/utils/application-interfaces.ts
Normal file
17
src/lib/utils/application-interfaces.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export interface IEndpointDatabase {
|
||||
|
||||
// TODO: change return type to endpoint
|
||||
initDB(): boolean
|
||||
getEndpoint(name: string): boolean
|
||||
addEndpoint(name: string): boolean
|
||||
deleteEndpoint(name: string): boolean
|
||||
modifyEndpoint(name: string): boolean
|
||||
|
||||
}
|
||||
|
||||
export interface IApplicationSettingsDatabase {
|
||||
// TODO: specify settings, if any
|
||||
initDB(): boolean
|
||||
getSetting(name: string): any
|
||||
setSetting(name: string, value: any)
|
||||
}
|
||||
2
src/lib/utils/constants.ts
Normal file
2
src/lib/utils/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const PKG = __PKG__
|
||||
export const DB_PATH = "src/db/db.sqlite"
|
||||
0
src/lib/utils/endpoint.ts
Normal file
0
src/lib/utils/endpoint.ts
Normal file
0
src/lib/utils/firegex.ts
Normal file
0
src/lib/utils/firegex.ts
Normal file
43
src/lib/utils/nginx-utils.ts
Normal file
43
src/lib/utils/nginx-utils.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { $ } from "bun";
|
||||
|
||||
|
||||
export async function reloadNginx() {
|
||||
|
||||
if (!await validateSchema()) {
|
||||
const output = await $`nginx -t 2>&1`
|
||||
.nothrow()
|
||||
.text()
|
||||
// UGLY: make this a specific error
|
||||
throw new Error(output)
|
||||
}
|
||||
|
||||
// Start nginx, should be side-effect free
|
||||
startNginx()
|
||||
const output = await $`rc-service nginx reload`.text()
|
||||
}
|
||||
|
||||
export async function startNginx() {
|
||||
|
||||
if (!await validateSchema()) {
|
||||
const output = await $`nginx -t 2>&1`
|
||||
.nothrow()
|
||||
.text()
|
||||
// UGLY: make this a specific error
|
||||
throw new Error(output)
|
||||
}
|
||||
|
||||
const output = await $`rc-service nginx start`.text()
|
||||
}
|
||||
|
||||
export async function validateSchema() {
|
||||
|
||||
const output = await $`nginx -t 2>&1`.nothrow().text()
|
||||
console.log(output)
|
||||
|
||||
const successRegex = new RegExp("test is successful", "gm")
|
||||
|
||||
const result = successRegex.test(output)
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
70
src/lib/utils/ports-utils.ts
Normal file
70
src/lib/utils/ports-utils.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { $ } from "bun";
|
||||
|
||||
/**
|
||||
* This methods runs `netstat -ltun`
|
||||
* to take all ports occupied by
|
||||
* other services on the host machine
|
||||
*
|
||||
* Since this is run in docker with
|
||||
* network mode `host`, it takes all
|
||||
* ports
|
||||
*
|
||||
* @returns occupied ports
|
||||
*/
|
||||
export async function portScan(): Promise<Set<number>> {
|
||||
|
||||
const portRegex = new RegExp("(?:\:)(?<port_number>[0-9]+)", "gm")
|
||||
const netstatOutput : string = await $`netstat -ltun`.text()
|
||||
const ports = netstatOutput.matchAll(portRegex)
|
||||
|
||||
const portArray : Set<number>= new Set()
|
||||
|
||||
for (const match of ports) {
|
||||
portArray.add(
|
||||
Number(match[1])
|
||||
)
|
||||
}
|
||||
|
||||
return portArray
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the port is actually a port,
|
||||
* throws otherwise
|
||||
* @param port_number port to validate
|
||||
*/
|
||||
export function validatePort(port_number: number): void {
|
||||
// Validate against Float
|
||||
if (port_number % 1 !== 0) {
|
||||
throw new Error("The specified port is not an Integer")
|
||||
}
|
||||
|
||||
// Validate for range
|
||||
if (port_number < 1 || port_number > 65535 ) {
|
||||
throw new Error("The specified port is not in the range 1...65535")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method checks for the availability of a port
|
||||
*
|
||||
* Throws if the port is not a valid port
|
||||
*
|
||||
* @param port_number port to check
|
||||
* @returns `true` if port is available, `false` otherwise
|
||||
*/
|
||||
export async function isPortAvailable(port_number: number): Promise<boolean> {
|
||||
|
||||
validatePort(port_number)
|
||||
|
||||
const occupied_ports = await portScan()
|
||||
|
||||
// Validate for availability
|
||||
if (occupied_ports.has(port_number)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
11
src/routes/+page.svelte
Normal file
11
src/routes/+page.svelte
Normal file
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<h1>SSL-Sniffer</h1>
|
||||
<p>A Sniffer for all your needs</p>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
47
src/routes/api/endpoint/ssl-termination/[name]/+server.ts
Normal file
47
src/routes/api/endpoint/ssl-termination/[name]/+server.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { error, json, text } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
/***********************************************************
|
||||
*
|
||||
* Author: Christian Risi 26/06/2025
|
||||
*
|
||||
* There is no POST handling here as it's not
|
||||
* idempotent, so semantically it's better a
|
||||
* PUT instead of a POST
|
||||
*
|
||||
***********************************************************/
|
||||
|
||||
export const GET: RequestHandler = async ({ request }) => {
|
||||
return json(1)
|
||||
}
|
||||
|
||||
export const PUT: RequestHandler = async ({ request }) => {
|
||||
return json(1)
|
||||
}
|
||||
|
||||
export const PATCH: RequestHandler = async ({ request }) => {
|
||||
|
||||
return json(1)
|
||||
}
|
||||
|
||||
export const DELETE: RequestHandler = async ({ request, }) => {
|
||||
|
||||
// TODO: make it delete the resource
|
||||
return json(1)
|
||||
}
|
||||
|
||||
export const fallback: RequestHandler = async ({ request }) => {
|
||||
|
||||
// TODO: return method not allowed
|
||||
const res = new Response(
|
||||
null,
|
||||
{
|
||||
status: 405,
|
||||
statusText: "Method Not Allowed",
|
||||
headers: {
|
||||
Allow: "GET, PUT, PATCH, DELETE"
|
||||
}
|
||||
}
|
||||
)
|
||||
return res
|
||||
};
|
||||
69
src/routes/api/reload/+server.ts
Normal file
69
src/routes/api/reload/+server.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { error, type json, type RequestHandler } from "@sveltejs/kit";
|
||||
|
||||
// UGLY: this should be more flexible
|
||||
class ReloadNginxReq {
|
||||
|
||||
public auth: string
|
||||
public nginx: string
|
||||
|
||||
constructor(
|
||||
json: any
|
||||
) {
|
||||
if (!json) {
|
||||
throw new Error("This is an invalid JSON")
|
||||
}
|
||||
|
||||
if (!json.nginx || !json.auth) {
|
||||
throw new Error("Can't parse this JSON")
|
||||
}
|
||||
|
||||
this.auth = json.auth
|
||||
this.nginx = json.nginx
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
|
||||
let parsedReq : ReloadNginxReq
|
||||
|
||||
try {
|
||||
parsedReq = new ReloadNginxReq(
|
||||
await request.json()
|
||||
)
|
||||
} catch (error) {
|
||||
return new Response(null, {
|
||||
status: 400,
|
||||
statusText: "Bad Request"
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Reload Data about Nginx
|
||||
|
||||
// TODO: Notify frontends
|
||||
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
statusText: "OK"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const fallback: RequestHandler = async () => {
|
||||
|
||||
// TODO: return method not allowed
|
||||
const res = new Response(
|
||||
null,
|
||||
{
|
||||
status: 405,
|
||||
statusText: "Method Not Allowed",
|
||||
headers: {
|
||||
Allow: "POST"
|
||||
}
|
||||
}
|
||||
)
|
||||
return res
|
||||
};
|
||||
9
src/routes/api/version/+server.ts
Normal file
9
src/routes/api/version/+server.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { PKG } from '$lib';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export function GET() {
|
||||
|
||||
const version = `SSL-Sniffer version ${PKG.version}`
|
||||
return json(version)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user