From 60069c86af4b7fccbd3d47bb0502d035f2a4eba1 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Sun, 5 Mar 2023 18:00:03 +0100 Subject: [PATCH] server Tag --- src/api/acts/client.ts | 7 ++- src/api/acts/login.ts | 16 +++---- src/api/post.ts | 13 +++++- src/api/user.ts | 18 ++++---- src/api/ws.ts | 5 ++- src/main.ts | 17 +++++--- src/server/outbagURL.ts | 92 +++++++++++++++++++-------------------- src/server/serverCerts.ts | 84 ++++++++++++++++++++++++----------- src/sys/config.ts | 30 ++++++++++++- src/sys/db.ts | 11 +++-- 10 files changed, 183 insertions(+), 110 deletions(-) diff --git a/src/api/acts/client.ts b/src/api/acts/client.ts index 5d3ef5f..b2144f3 100644 --- a/src/api/acts/client.ts +++ b/src/api/acts/client.ts @@ -1,7 +1,6 @@ import { eq, select, update } from "dblang"; -import { outbagURLshort } from "../../server/outbagURL.js"; import { PERMISSIONS } from "../../server/permissions.js"; -import { oConf } from "../../sys/config.js"; +import { selfTag } from "../../sys/config.js"; import { sha256, sign } from "../../sys/crypto.js"; import { accounts, db } from "../../sys/db.js"; import { getSettings, SETTINGS } from "../../sys/settings.js"; @@ -30,8 +29,8 @@ export const createSignature = { publicKey: "string" }, func: async (client: Client, data: any, aws: (code: string, data: any) => void) => { - const server = await outbagURLshort(oConf.get("System", "URL") + ":" + oConf.get("System", "PORTexposed")); - const tag = client.name + "@" + server + "-" + data.publicKey; + const server = selfTag; + const tag = `${client.name}@${server.host}:${server.port}-${data.publicKey}`; var signature = await sign(tag, await getSettings(SETTINGS.privateKey)); aws("ok", signature); } diff --git a/src/api/acts/login.ts b/src/api/acts/login.ts index c34b77d..b03e7d7 100644 --- a/src/api/acts/login.ts +++ b/src/api/acts/login.ts @@ -1,8 +1,8 @@ import { select, count, alias, insert, remove, le, update, minus, eq, and } from "dblang"; -import { outbagURLshort } from "../../server/outbagURL.js"; +import { outbagURLfromTag } from "../../server/outbagURL.js"; import { PERMISSIONS } from "../../server/permissions.js"; import { getRemote } from "../../server/serverCerts.js"; -import { oConf } from "../../sys/config.js"; +import { localhostTag, oConf } from "../../sys/config.js"; import { sha256, verify } from "../../sys/crypto.js"; import { accounts, db, signupOTA as signupOTATable } from "../../sys/db.js"; import { get64, uts } from "../../sys/tools.js"; @@ -50,7 +50,7 @@ export const signup: Act = { client.state = STATE.client; client.accID = accID; client.name = data.name; - client.server = "localhost"; + client.server = localhostTag; } } else { client.suspect(); @@ -92,7 +92,7 @@ export const signupOTA = { client.state = STATE.client; client.accID = accID; client.name = data.name; - client.server = "localhost"; + client.server = localhostTag; } } else { client.suspect(); @@ -126,7 +126,7 @@ export const signin = { client.state = STATE.client; client.accID = accID; client.name = data.name; - client.server = "localhost"; + client.server = localhostTag; } } }; @@ -141,9 +141,9 @@ export const remote1 = { sign: "string", }, func: async (client: Client, data: any, aws: (code: string, data: any) => void) => { - data.server = await outbagURLshort(data.server); + let server = await outbagURLfromTag(data.server); try { - var cert = await getRemote(data.server); + var cert = await getRemote(server); var tagAcert = data.name + "@" + data.server + "-" + data.key; if (!(await verify(tagAcert, data.sign, cert))) { client.suspect(); @@ -152,7 +152,7 @@ export const remote1 = { } client.name = data.name; - client.server = data.server; + client.server = server; client.challenge = get64(64); client.state = STATE.remoteP; client.remoteKey = data.key; diff --git a/src/api/post.ts b/src/api/post.ts index ffe32f2..27356d8 100644 --- a/src/api/post.ts +++ b/src/api/post.ts @@ -10,15 +10,24 @@ import { sha256 } from "../sys/crypto.js"; import { get64, uts } from "../sys/tools.js"; import { addShutdownTask } from "nman"; import { suspectRequest } from "../sys/bruteforce.js"; +import { localhostTag } from "../sys/config.js"; let acts = importActs as { [key: string]: Act }; let tempTokens: { [key: string]: postClient } = {}; +let activePost = false; +export const activatePost = () => { activePost = true; }; + export const addPostMethods = (server: express.Express) => { for (const act in acts) { let methode = acts[act]; server.post("/api/" + act, async (req: suspectRequest, res) => { + if (!activePost) { + res.status(500); + res.send("not active"); + return; + } debug("POST", "reveived:", req.body); const aws = (state: string, data: any) => { res.status(state == "error" ? 400 : 200); @@ -29,7 +38,7 @@ export const addPostMethods = (server: express.Express) => { try { let auth = authorization.parse(req.headers["authorization"] ?? ""); if (auth.scheme == "outbagServer") { - + } else if (auth.token != null && typeof auth.token == "string") { if (tempTokens[auth.token] != null) { client = tempTokens[auth.token]; @@ -41,7 +50,7 @@ export const addPostMethods = (server: express.Express) => { } else if (auth?.params?.name != null && auth?.params?.accountKey != null && typeof auth?.params?.name == "string" && typeof auth?.params?.accountKey == "string") { client = new postClient(req.ip); client.name = auth?.params?.name; - client.server = "localhost"; + client.server = localhostTag; let accountKey = auth?.params?.accountKey; let query = await select([accounts.accID, accounts.accountKey, accounts.accountKeySalt], accounts) diff --git a/src/api/user.ts b/src/api/user.ts index b561ffc..2d0c27a 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,17 +1,18 @@ import { and, eq, naturalJoin, select, update } from "dblang"; import { accounts, db, roomMembers, rooms } from "../sys/db.js"; import { addBruteforcePotantial } from "../sys/bruteforce.js"; +import { outbagServer } from "../server/outbagURL.js"; export const STATE = { - no: 0b00001, + no: 0b00001, remoteP: 0b00010, - remote: 0b00100, - client: 0b01000, - server: 0b10000, + remote: 0b00100, + client: 0b01000, + server: 0b10000, }; export const MODE = { - ws: 0b01, + ws: 0b01, post: 0b10, both: 0b11, }; @@ -27,7 +28,7 @@ export type Act = { export class Client { name: string = ""; - server: string = ""; + server: outbagServer; ip: string = ""; state: number = STATE.no; @@ -36,6 +37,7 @@ export class Client { remoteKey: string = ""; constructor(ip: string) { + this.server = new outbagServer("", "", "", ""); this.ip = ip; } @@ -49,7 +51,7 @@ export class Client { .where(and( eq(rooms.name, name), eq(roomMembers.name, this.name), - eq(roomMembers.server, this.server) + eq(roomMembers.server, this.server.tag) )) .query(db); if (query.length == 0) return -1; @@ -62,7 +64,7 @@ export class Client { eq(rooms.name, name), eq(roomMembers.admin, true), eq(roomMembers.name, this.name), - eq(roomMembers.server, this.server) + eq(roomMembers.server, this.server.tag) )) .query(db); if (query.length == 0) return -1; diff --git a/src/api/ws.ts b/src/api/ws.ts index ca978fb..d5200da 100644 --- a/src/api/ws.ts +++ b/src/api/ws.ts @@ -7,9 +7,12 @@ import * as importActs from "./acts.js" let acts = importActs as { [key: string]: Act }; +let activeWS = false; +export const activatePost = () => { activeWS = true; }; + export const wsOnConnection = (socket: ws.WebSocket, req: http.IncomingMessage) => { let ip = req.socket.remoteAddress; - if (bruteforcecheck(ip ?? "")) return void socket.close(); + if (!activeWS || bruteforcecheck(ip ?? "")) return void socket.close(); new wsClient(socket, req); } diff --git a/src/main.ts b/src/main.ts index 3ef15e2..4905b89 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,15 +8,15 @@ import nman from "nman"; import cors from "cors"; import { WebSocketServer } from "ws"; import { Command } from "commander"; -import { oConf } from "./sys/config.js"; +import { generateTag, oConf } from "./sys/config.js"; import { error, log } from "./sys/log.js"; import { connectToDB } from "./sys/db.js"; -import bruteforce, { suspectRequest } from "./sys/bruteforce.js"; +import bruteforce from "./sys/bruteforce.js"; import { fullSetup, partiellSetup } from "./setup/config.js"; import { addGetMethods } from "./api/get.js"; import { addPostMethods } from "./api/post.js"; import { wsOnConnection } from "./api/ws.js"; -import { startUpdateCerts } from "./server/serverCerts.js"; +import { startUpdateCert } from "./server/serverCerts.js"; const config = { @@ -52,6 +52,8 @@ program program.parse(); +const activeRequest = false; + var serverclose = { close: (d: () => void) => d() }; nman.addShutdownTask(() => new Promise(async (res, rej) => { //await closeWebSocket(); @@ -96,6 +98,9 @@ async function startServer() { } -function complete_loaded() { - startUpdateCerts(); -} \ No newline at end of file +async function complete_loaded() { + startUpdateCert(); + let succ = await generateTag(); + if(!succ) error("System", "Could not resolve own Server Tag. Remote-Auth will not work! Check if the Server is reachable and the config ist correct!"); + +} \ No newline at end of file diff --git a/src/server/outbagURL.ts b/src/server/outbagURL.ts index b75a9ac..de70f3d 100644 --- a/src/server/outbagURL.ts +++ b/src/server/outbagURL.ts @@ -1,59 +1,55 @@ const WELL_KNOWN_PATH = "/.well-known/outbag/server"; -const DEFAULT_PORT = 7223; +const DEFAULT_PORT = "7223"; -export const outbagURL = (url: string, prefix = "https") => new Promise((res, rej) => { +const fetchWellknown = async (uri: URL) => { + uri = new URL(uri); + let resp = await fetch(uri); + let json = await resp.json(); + if (json.path == null || json.port == null) throw new Error("NotAValidWellKnown"); + return { host: uri.hostname, path: json.path as string, port: json.port as string }; +}; + +export const outbagURLfromTag = async (tag: string) => { let uri: URL; try { - uri = new URL("/", url); + uri = new URL("/", tag); uri.protocol = "https"; } catch (_) { - uri = new URL("https://" + url); + uri = new URL("https://" + tag); } uri.pathname = WELL_KNOWN_PATH; - fetch(uri) - .then(resp => resp.json()) - .then(({ path = "/", port = 7223 }) => { - uri.pathname = path; - uri.port = port; - uri.protocol = prefix; - res(uri.toString()); - }) - .catch(_ => { - if (uri.port == '') { - uri.port = DEFAULT_PORT + ""; - outbagURL(uri.toString(), prefix) - .then(url => res(url)) - .catch(_ => rej()); - } else { - rej(); - } - }); -}); - -export const outbagURLshort = (url: string) => new Promise((res, rej) => { - let uri: URL; + let isMain = uri.port == ''; try { - uri = new URL("/", url); - uri.protocol = "https"; + let { host, path, port } = await fetchWellknown(uri); + return new outbagServer(isMain ? host : host + ":" + port, host, path, port); } catch (_) { - uri = new URL("https://" + url); + if (isMain) { + try { + uri.port = DEFAULT_PORT; + let { host, path, port } = await fetchWellknown(uri); + return new outbagServer(isMain ? host : host + ":" + port, host, path, port); + } catch (_) { } + } + throw new Error("InvalidOutbagServer"); } - uri.pathname = WELL_KNOWN_PATH; - fetch(uri) - .then(resp => resp.json()) - .then(({ path = "/", port = 7223 }) => { - uri.pathname = path; - uri.port = port; - res(uri.hostname + ":" + uri.port); - }) - .catch(_ => { - if (uri.port == '') { - uri.port = DEFAULT_PORT + ""; - outbagURLshort(uri.toString()) - .then(url => res(url)) - .catch(_ => rej()); - } else { - rej(); - } - }); -}); \ No newline at end of file +}; + +export class outbagServer { + host: string; + port: string; + path: string; + tag: string; + constructor(tag: string, host: string, path: string, port: string) { + this.host = host; + this.port = port; + if (!path.startsWith("/")) this.path = "/" + path; + else this.path = path; + this.tag = tag; + } + get httpsURL() { + return `https://${this.host}:${this.port}${this.path}/`; + } + get wsURL() { + return `wss://${this.host}:${this.port}${this.path}/`; + } +}; diff --git a/src/server/serverCerts.ts b/src/server/serverCerts.ts index d402027..cb4bae0 100644 --- a/src/server/serverCerts.ts +++ b/src/server/serverCerts.ts @@ -1,14 +1,14 @@ -import { outbagURL, outbagURLshort } from "./outbagURL.js"; -import { db, serverCerts } from "../sys/db.js" -import { eq, insert, select, update } from "dblang"; -import { error, log } from "../sys/log.js"; +import { outbagServer } from "./outbagURL.js"; +import { debug, log } from "../sys/log.js"; import { uts } from "../sys/tools.js"; import { getSettings, setSettings, SETTINGS } from "../sys/settings.js" import { generateSigningKey } from "../sys/crypto.js" import { oConf } from "../sys/config.js"; -import nman from "nman"; +import nman, { addShutdownTask } from "nman"; +import { insert } from "dblang"; +import { db, servers } from "../sys/db.js"; -export const startUpdateCerts = () => { +export const startUpdateCert = () => { const update = async () => { var exp = parseInt(await getSettings(SETTINGS.certExpires)); if (uts() - 60 >= (exp || 0)) { @@ -27,26 +27,25 @@ export const startUpdateCerts = () => { }, 100); } -async function updateRemote(url: string, pKey: string = ""): Promise { - var urlP = await outbagURL(url); +/*async function updateRemote(server: outbagServer): Promise { return new Promise((res, rej) => { - fetch(urlP + "api/server/publicKey") + fetch(server.httpsURL + "api/server/publicKey") .then(d => d.json()) .then(async json => { let { publicKey, expires } = json; if (typeof publicKey == "string" && typeof expires == "string") { try { - await insert(serverCerts.url, serverCerts.publicKey, serverCerts.expires) - .add(url, publicKey, expires) + await insert(servers.url, servers.publicKey, servers.expires) + .add(server.tag, publicKey, expires) .query(db); } catch (error) { - await update(serverCerts) - .set(serverCerts.publicKey, publicKey) - .set(serverCerts.expires, expires) - .where(eq(serverCerts.url, url)) + await update(servers) + .set(servers.publicKey, publicKey) + .set(servers.expires, expires) + .where(eq(servers.url, server.tag)) .query(db); } - res(publicKey); + res(true); } else { if (publicKey.length > 0) { res(publicKey); @@ -57,20 +56,53 @@ async function updateRemote(url: string, pKey: string = ""): Promise { error("serverCert", "fetch error:", e); - if (pKey.length > 0) { - res(pKey); - return; - } res(false); }); }); }; -export const getRemote = async (url: string) => { - let query = await select([serverCerts.publicKey, serverCerts.expires], serverCerts) - .where(eq(serverCerts.url, url)) +export const getRemote = async (server: outbagServer) => { + await updateRemote(server); + let query = await select([servers.publicKey, servers.expires], servers) + .where(eq(servers.url, server.tag)) .query(db); - if (query.length == 0 || query[0][serverCerts.expires] < uts() - 60) - return await updateRemote(url, query[0][serverCerts.publicKey]); - return query[0][serverCerts.publicKey]; + return query[0][servers.publicKey]; +};*/ +const deleteafter = 60 * 60; + +const certList: { [key: string]: { exp: number, cert: string } } = {}; + +var certListCleaner = setInterval(async () => { + var utst = uts(); + let keys = Object.keys(certList); + for (var i = 0; i < keys.length; i++) { + if (utst >= certList[keys[i]].exp) { + debug("Certificate List", "remove Cert: ", keys[i]); + delete certList[keys[i]]; + } + } +}, 1000 * 60); +addShutdownTask(() => clearInterval(certListCleaner), 5000); + +const updateCert = async (server: outbagServer) => { + try { + let resp = await fetch(server.httpsURL + "api/server/publicKey") + let json = await resp.json(); + let { publicKey, expires } = json; + certList[server.tag] = { exp: Math.min(expires, deleteafter), cert: publicKey }; + try { + await insert(servers.tag) + .add(server.tag) + .query(db); + } catch (error) {} + return true; + } catch (error) { + return false; + } }; + +export const getRemote = async (server: outbagServer) => { + if (certList[server.tag] == null || certList[server.tag].exp >= uts()) await updateCert(server); + if (certList[server.tag] != null) return certList[server.tag].cert; + else throw new Error("Cert Error"); +}; \ No newline at end of file diff --git a/src/sys/config.ts b/src/sys/config.ts index 809b3f3..39df4d9 100644 --- a/src/sys/config.ts +++ b/src/sys/config.ts @@ -1,4 +1,5 @@ import juml from "juml"; +import { outbagServer, outbagURLfromTag } from "../server/outbagURL.js"; const conf_struct = { System: { @@ -29,4 +30,31 @@ const conf_struct = { } }; -export const oConf = new juml(conf_struct); \ No newline at end of file +export const oConf = new juml(conf_struct); + +export let localhostTag: outbagServer = new outbagServer( + "localhost", + oConf.get("System", "URL")+"", + oConf.get("System", "PATHexposed")+"", + oConf.get("System", "PORTexposed")+"", +); + +export let selfTag: outbagServer = localhostTag; + +export const generateTag = async () => { + try { + let mainServerHost: outbagServer | null = null; + try { + mainServerHost = await outbagURLfromTag(oConf.get("System", "URL")); + } catch (error) {} + let serverHostPort = await outbagURLfromTag( + oConf.get("System", "URL") + ":" + oConf.get("System", "PORTexposed")); + if(mainServerHost == null || mainServerHost != serverHostPort){ + selfTag = serverHostPort; + }else selfTag = mainServerHost; + return true; + } catch (error) { + return false; + } + +}; \ No newline at end of file diff --git a/src/sys/db.ts b/src/sys/db.ts index 8985c10..fca31e5 100644 --- a/src/sys/db.ts +++ b/src/sys/db.ts @@ -67,12 +67,11 @@ settings.addAttributes({ data: { type: TEXT }, }) -export const serverCerts = db.newTable("serverCerts"); -serverCerts.addAttributes({ - serverCertID: { type: INT, primaryKey: true, autoIncrement: true }, - url: { type: TEXT }, - publicKey: { type: TEXT }, - expires: { type: BIGINT }, +export const servers = db.newTable("servers"); +servers.addAttributes({ + tag: { type: VARCHAR(255), primaryKey: true }, + //publicKey: { type: TEXT }, + //expires: { type: BIGINT }, token: { type: TEXT, notNull: false }, });