server Tag

This commit is contained in:
jusax23 2023-03-05 18:00:03 +01:00
parent 8accb3fd2f
commit 60069c86af
Signed by: jusax23
GPG key ID: 499E2AA870C1CD41
10 changed files with 183 additions and 110 deletions

View file

@ -1,7 +1,6 @@
import { eq, select, update } from "dblang"; import { eq, select, update } from "dblang";
import { outbagURLshort } from "../../server/outbagURL.js";
import { PERMISSIONS } from "../../server/permissions.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 { sha256, sign } from "../../sys/crypto.js";
import { accounts, db } from "../../sys/db.js"; import { accounts, db } from "../../sys/db.js";
import { getSettings, SETTINGS } from "../../sys/settings.js"; import { getSettings, SETTINGS } from "../../sys/settings.js";
@ -30,8 +29,8 @@ export const createSignature = {
publicKey: "string" publicKey: "string"
}, },
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => { 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 server = selfTag;
const tag = client.name + "@" + server + "-" + data.publicKey; const tag = `${client.name}@${server.host}:${server.port}-${data.publicKey}`;
var signature = await sign(tag, await getSettings(SETTINGS.privateKey)); var signature = await sign(tag, await getSettings(SETTINGS.privateKey));
aws("ok", signature); aws("ok", signature);
} }

View file

@ -1,8 +1,8 @@
import { select, count, alias, insert, remove, le, update, minus, eq, and } from "dblang"; 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 { PERMISSIONS } from "../../server/permissions.js";
import { getRemote } from "../../server/serverCerts.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 { sha256, verify } from "../../sys/crypto.js";
import { accounts, db, signupOTA as signupOTATable } from "../../sys/db.js"; import { accounts, db, signupOTA as signupOTATable } from "../../sys/db.js";
import { get64, uts } from "../../sys/tools.js"; import { get64, uts } from "../../sys/tools.js";
@ -50,7 +50,7 @@ export const signup: Act = {
client.state = STATE.client; client.state = STATE.client;
client.accID = accID; client.accID = accID;
client.name = data.name; client.name = data.name;
client.server = "localhost"; client.server = localhostTag;
} }
} else { } else {
client.suspect(); client.suspect();
@ -92,7 +92,7 @@ export const signupOTA = {
client.state = STATE.client; client.state = STATE.client;
client.accID = accID; client.accID = accID;
client.name = data.name; client.name = data.name;
client.server = "localhost"; client.server = localhostTag;
} }
} else { } else {
client.suspect(); client.suspect();
@ -126,7 +126,7 @@ export const signin = {
client.state = STATE.client; client.state = STATE.client;
client.accID = accID; client.accID = accID;
client.name = data.name; client.name = data.name;
client.server = "localhost"; client.server = localhostTag;
} }
} }
}; };
@ -141,9 +141,9 @@ export const remote1 = {
sign: "string", sign: "string",
}, },
func: async (client: Client, data: any, aws: (code: string, data: any) => void) => { 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 { try {
var cert = await getRemote(data.server); var cert = await getRemote(server);
var tagAcert = data.name + "@" + data.server + "-" + data.key; var tagAcert = data.name + "@" + data.server + "-" + data.key;
if (!(await verify(tagAcert, data.sign, cert))) { if (!(await verify(tagAcert, data.sign, cert))) {
client.suspect(); client.suspect();
@ -152,7 +152,7 @@ export const remote1 = {
} }
client.name = data.name; client.name = data.name;
client.server = data.server; client.server = server;
client.challenge = get64(64); client.challenge = get64(64);
client.state = STATE.remoteP; client.state = STATE.remoteP;
client.remoteKey = data.key; client.remoteKey = data.key;

View file

@ -10,15 +10,24 @@ import { sha256 } from "../sys/crypto.js";
import { get64, uts } from "../sys/tools.js"; import { get64, uts } from "../sys/tools.js";
import { addShutdownTask } from "nman"; import { addShutdownTask } from "nman";
import { suspectRequest } from "../sys/bruteforce.js"; import { suspectRequest } from "../sys/bruteforce.js";
import { localhostTag } from "../sys/config.js";
let acts = importActs as { [key: string]: Act }; let acts = importActs as { [key: string]: Act };
let tempTokens: { [key: string]: postClient } = {}; let tempTokens: { [key: string]: postClient } = {};
let activePost = false;
export const activatePost = () => { activePost = true; };
export const addPostMethods = (server: express.Express) => { export const addPostMethods = (server: express.Express) => {
for (const act in acts) { for (const act in acts) {
let methode = acts[act]; let methode = acts[act];
server.post("/api/" + act, async (req: suspectRequest, res) => { server.post("/api/" + act, async (req: suspectRequest, res) => {
if (!activePost) {
res.status(500);
res.send("not active");
return;
}
debug("POST", "reveived:", req.body); debug("POST", "reveived:", req.body);
const aws = (state: string, data: any) => { const aws = (state: string, data: any) => {
res.status(state == "error" ? 400 : 200); res.status(state == "error" ? 400 : 200);
@ -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") { } 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 = new postClient(req.ip);
client.name = auth?.params?.name; client.name = auth?.params?.name;
client.server = "localhost"; client.server = localhostTag;
let accountKey = auth?.params?.accountKey; let accountKey = auth?.params?.accountKey;
let query = await select([accounts.accID, accounts.accountKey, accounts.accountKeySalt], accounts) let query = await select([accounts.accID, accounts.accountKey, accounts.accountKeySalt], accounts)

View file

@ -1,17 +1,18 @@
import { and, eq, naturalJoin, select, update } from "dblang"; import { and, eq, naturalJoin, select, update } from "dblang";
import { accounts, db, roomMembers, rooms } from "../sys/db.js"; import { accounts, db, roomMembers, rooms } from "../sys/db.js";
import { addBruteforcePotantial } from "../sys/bruteforce.js"; import { addBruteforcePotantial } from "../sys/bruteforce.js";
import { outbagServer } from "../server/outbagURL.js";
export const STATE = { export const STATE = {
no: 0b00001, no: 0b00001,
remoteP: 0b00010, remoteP: 0b00010,
remote: 0b00100, remote: 0b00100,
client: 0b01000, client: 0b01000,
server: 0b10000, server: 0b10000,
}; };
export const MODE = { export const MODE = {
ws: 0b01, ws: 0b01,
post: 0b10, post: 0b10,
both: 0b11, both: 0b11,
}; };
@ -27,7 +28,7 @@ export type Act = {
export class Client { export class Client {
name: string = ""; name: string = "";
server: string = ""; server: outbagServer;
ip: string = ""; ip: string = "";
state: number = STATE.no; state: number = STATE.no;
@ -36,6 +37,7 @@ export class Client {
remoteKey: string = ""; remoteKey: string = "";
constructor(ip: string) { constructor(ip: string) {
this.server = new outbagServer("", "", "", "");
this.ip = ip; this.ip = ip;
} }
@ -49,7 +51,7 @@ export class Client {
.where(and( .where(and(
eq(rooms.name, name), eq(rooms.name, name),
eq(roomMembers.name, this.name), eq(roomMembers.name, this.name),
eq(roomMembers.server, this.server) eq(roomMembers.server, this.server.tag)
)) ))
.query(db); .query(db);
if (query.length == 0) return -1; if (query.length == 0) return -1;
@ -62,7 +64,7 @@ export class Client {
eq(rooms.name, name), eq(rooms.name, name),
eq(roomMembers.admin, true), eq(roomMembers.admin, true),
eq(roomMembers.name, this.name), eq(roomMembers.name, this.name),
eq(roomMembers.server, this.server) eq(roomMembers.server, this.server.tag)
)) ))
.query(db); .query(db);
if (query.length == 0) return -1; if (query.length == 0) return -1;

View file

@ -7,9 +7,12 @@ import * as importActs from "./acts.js"
let acts = importActs as { [key: string]: Act }; let acts = importActs as { [key: string]: Act };
let activeWS = false;
export const activatePost = () => { activeWS = true; };
export const wsOnConnection = (socket: ws.WebSocket, req: http.IncomingMessage) => { export const wsOnConnection = (socket: ws.WebSocket, req: http.IncomingMessage) => {
let ip = req.socket.remoteAddress; let ip = req.socket.remoteAddress;
if (bruteforcecheck(ip ?? "")) return void socket.close(); if (!activeWS || bruteforcecheck(ip ?? "")) return void socket.close();
new wsClient(socket, req); new wsClient(socket, req);
} }

View file

@ -8,15 +8,15 @@ import nman from "nman";
import cors from "cors"; import cors from "cors";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
import { Command } from "commander"; 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 { error, log } from "./sys/log.js";
import { connectToDB } from "./sys/db.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 { fullSetup, partiellSetup } from "./setup/config.js";
import { addGetMethods } from "./api/get.js"; import { addGetMethods } from "./api/get.js";
import { addPostMethods } from "./api/post.js"; import { addPostMethods } from "./api/post.js";
import { wsOnConnection } from "./api/ws.js"; import { wsOnConnection } from "./api/ws.js";
import { startUpdateCerts } from "./server/serverCerts.js"; import { startUpdateCert } from "./server/serverCerts.js";
const config = { const config = {
@ -52,6 +52,8 @@ program
program.parse(); program.parse();
const activeRequest = false;
var serverclose = { close: (d: () => void) => d() }; var serverclose = { close: (d: () => void) => d() };
nman.addShutdownTask(() => new Promise(async (res, rej) => { nman.addShutdownTask(() => new Promise(async (res, rej) => {
//await closeWebSocket(); //await closeWebSocket();
@ -96,6 +98,9 @@ async function startServer() {
} }
function complete_loaded() { async function complete_loaded() {
startUpdateCerts(); 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!");
} }

View file

@ -1,59 +1,55 @@
const WELL_KNOWN_PATH = "/.well-known/outbag/server"; 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; let uri: URL;
try { try {
uri = new URL("/", url); uri = new URL("/", tag);
uri.protocol = "https"; uri.protocol = "https";
} catch (_) { } catch (_) {
uri = new URL("https://" + url); uri = new URL("https://" + tag);
} }
uri.pathname = WELL_KNOWN_PATH; uri.pathname = WELL_KNOWN_PATH;
fetch(uri) let isMain = uri.port == '';
.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;
try { try {
uri = new URL("/", url); let { host, path, port } = await fetchWellknown(uri);
uri.protocol = "https"; return new outbagServer(isMain ? host : host + ":" + port, host, path, port);
} catch (_) { } 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()) export class outbagServer {
.then(({ path = "/", port = 7223 }) => { host: string;
uri.pathname = path; port: string;
uri.port = port; path: string;
res(uri.hostname + ":" + uri.port); tag: string;
}) constructor(tag: string, host: string, path: string, port: string) {
.catch(_ => { this.host = host;
if (uri.port == '') { this.port = port;
uri.port = DEFAULT_PORT + ""; if (!path.startsWith("/")) this.path = "/" + path;
outbagURLshort(uri.toString()) else this.path = path;
.then(url => res(url)) this.tag = tag;
.catch(_ => rej()); }
} else { get httpsURL() {
rej(); return `https://${this.host}:${this.port}${this.path}/`;
} }
}); get wsURL() {
}); return `wss://${this.host}:${this.port}${this.path}/`;
}
};

View file

@ -1,14 +1,14 @@
import { outbagURL, outbagURLshort } from "./outbagURL.js"; import { outbagServer } from "./outbagURL.js";
import { db, serverCerts } from "../sys/db.js" import { debug, log } from "../sys/log.js";
import { eq, insert, select, update } from "dblang";
import { error, log } from "../sys/log.js";
import { uts } from "../sys/tools.js"; import { uts } from "../sys/tools.js";
import { getSettings, setSettings, SETTINGS } from "../sys/settings.js" import { getSettings, setSettings, SETTINGS } from "../sys/settings.js"
import { generateSigningKey } from "../sys/crypto.js" import { generateSigningKey } from "../sys/crypto.js"
import { oConf } from "../sys/config.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 () => { const update = async () => {
var exp = parseInt(await getSettings(SETTINGS.certExpires)); var exp = parseInt(await getSettings(SETTINGS.certExpires));
if (uts() - 60 >= (exp || 0)) { if (uts() - 60 >= (exp || 0)) {
@ -27,26 +27,25 @@ export const startUpdateCerts = () => {
}, 100); }, 100);
} }
async function updateRemote(url: string, pKey: string = ""): Promise<boolean | string> { /*async function updateRemote(server: outbagServer): Promise<boolean> {
var urlP = await outbagURL(url);
return new Promise((res, rej) => { return new Promise((res, rej) => {
fetch(urlP + "api/server/publicKey") fetch(server.httpsURL + "api/server/publicKey")
.then(d => d.json()) .then(d => d.json())
.then(async json => { .then(async json => {
let { publicKey, expires } = json; let { publicKey, expires } = json;
if (typeof publicKey == "string" && typeof expires == "string") { if (typeof publicKey == "string" && typeof expires == "string") {
try { try {
await insert(serverCerts.url, serverCerts.publicKey, serverCerts.expires) await insert(servers.url, servers.publicKey, servers.expires)
.add(url, publicKey, expires) .add(server.tag, publicKey, expires)
.query(db); .query(db);
} catch (error) { } catch (error) {
await update(serverCerts) await update(servers)
.set(serverCerts.publicKey, publicKey) .set(servers.publicKey, publicKey)
.set(serverCerts.expires, expires) .set(servers.expires, expires)
.where(eq(serverCerts.url, url)) .where(eq(servers.url, server.tag))
.query(db); .query(db);
} }
res(publicKey); res(true);
} else { } else {
if (publicKey.length > 0) { if (publicKey.length > 0) {
res(publicKey); res(publicKey);
@ -57,20 +56,53 @@ async function updateRemote(url: string, pKey: string = ""): Promise<boolean | s
}) })
.catch((e) => { .catch((e) => {
error("serverCert", "fetch error:", e); error("serverCert", "fetch error:", e);
if (pKey.length > 0) {
res(pKey);
return;
}
res(false); res(false);
}); });
}); });
}; };
export const getRemote = async (url: string) => { export const getRemote = async (server: outbagServer) => {
let query = await select([serverCerts.publicKey, serverCerts.expires], serverCerts) await updateRemote(server);
.where(eq(serverCerts.url, url)) let query = await select([servers.publicKey, servers.expires], servers)
.where(eq(servers.url, server.tag))
.query(db); .query(db);
if (query.length == 0 || query[0][serverCerts.expires] < uts() - 60) return query[0][servers.publicKey];
return await updateRemote(url, query[0][serverCerts.publicKey]); };*/
return query[0][serverCerts.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");
}; };

View file

@ -1,4 +1,5 @@
import juml from "juml"; import juml from "juml";
import { outbagServer, outbagURLfromTag } from "../server/outbagURL.js";
const conf_struct = { const conf_struct = {
System: { System: {
@ -30,3 +31,30 @@ const conf_struct = {
}; };
export const oConf = new juml(conf_struct); 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;
}
};

View file

@ -67,12 +67,11 @@ settings.addAttributes({
data: { type: TEXT }, data: { type: TEXT },
}) })
export const serverCerts = db.newTable("serverCerts"); export const servers = db.newTable("servers");
serverCerts.addAttributes({ servers.addAttributes({
serverCertID: { type: INT, primaryKey: true, autoIncrement: true }, tag: { type: VARCHAR(255), primaryKey: true },
url: { type: TEXT }, //publicKey: { type: TEXT },
publicKey: { type: TEXT }, //expires: { type: BIGINT },
expires: { type: BIGINT },
token: { type: TEXT, notNull: false }, token: { type: TEXT, notNull: false },
}); });