server improvements and actually fix masqr

This commit is contained in:
Riftriot 2024-02-12 13:18:37 -06:00
parent d36fc0fceb
commit 0813d6bfd9
6 changed files with 186 additions and 138 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
MASQR=1

View file

@ -19,7 +19,7 @@
If you see this page, the nginx web server is successfully installed and If you see this page, the nginx web server is successfully installed and
working. Further configuration is required. If you are expecting another working. Further configuration is required. If you are expecting another
page, please check your network or page, please check your network or
<a href="/"><b>Refresh this page</b></a> <a href="/" id="rcheck"><b>Refresh this page</b></a>
</p> </p>
<p> <p>
@ -30,5 +30,11 @@
</p> </p>
<p><em>Thank you for using nginx.</em></p> <p><em>Thank you for using nginx.</em></p>
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload()
localStorage["auth"] = 1
}
</script>
</body> </body>
</html> </html>

View file

@ -20,5 +20,14 @@
<body style="margin: 0"> <body style="margin: 0">
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/index.tsx"></script> <script type="module" src="/src/index.tsx"></script>
<script>
if (new URL(Object.assign(document.createElement('a'), {href: '/'}).href).password) window.location.href = window.location.href
</script>
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload()
localStorage["auth"] = 1
}
</script>
</body> </body>
</html> </html>

View file

@ -17,7 +17,9 @@
"@nebula-services/bare-server-node": "2.0.1-patch.1", "@nebula-services/bare-server-node": "2.0.1-patch.1",
"@nebula-services/dynamic": "0.7.2-patch.2", "@nebula-services/dynamic": "0.7.2-patch.2",
"@nebula-services/ultraviolet": "1.0.1-1.patch.7", "@nebula-services/ultraviolet": "1.0.1-1.patch.7",
"chalk": "^5.3.0",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"cookie-parser": "^1.4.6",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"fastify": "^4.25.1", "fastify": "^4.25.1",
"framer-motion": "^10.16.16", "framer-motion": "^10.16.16",

23
pnpm-lock.yaml generated
View file

@ -23,9 +23,15 @@ dependencies:
'@nebula-services/ultraviolet': '@nebula-services/ultraviolet':
specifier: 1.0.1-1.patch.7 specifier: 1.0.1-1.patch.7
version: 1.0.1-1.patch.7 version: 1.0.1-1.patch.7
chalk:
specifier: ^5.3.0
version: 5.3.0
classnames: classnames:
specifier: ^2.3.2 specifier: ^2.3.2
version: 2.3.2 version: 2.3.2
cookie-parser:
specifier: ^1.4.6
version: 1.4.6
crypto-js: crypto-js:
specifier: ^4.2.0 specifier: ^4.2.0
version: 4.2.0 version: 4.2.0
@ -2225,11 +2231,28 @@ packages:
/convert-source-map@2.0.0: /convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
/cookie-parser@1.4.6:
resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==}
engines: {node: '>= 0.8.0'}
dependencies:
cookie: 0.4.1
cookie-signature: 1.0.6
dev: false
/cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
dev: false
/cookie-signature@1.2.1: /cookie-signature@1.2.1:
resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==} resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==}
engines: {node: '>=6.6.0'} engines: {node: '>=6.6.0'}
dev: false dev: false
/cookie@0.4.1:
resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==}
engines: {node: '>= 0.6'}
dev: false
/cookie@0.5.0: /cookie@0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}

279
server.ts
View file

@ -1,160 +1,167 @@
import fastify from "fastify"; import { createBareServer } from '@nebula-services/bare-server-node';
import fastifyStatic from "@fastify/static"; import chalk from "chalk";
import express from 'express';
import { createServer } from 'node:http';
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import createRammerhead from 'rammerhead/src/server/index.js';
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import cookieParser from "@fastify/cookie"; import cookieParser from "cookie-parser";
import { createServer } from "http";
import { createBareServer } from "@nebula-services/bare-server-node";
import createRammerhead from "rammerhead/src/server/index.js";
import wisp from "wisp-server-node"; import wisp from "wisp-server-node";
import { Socket } from "net"; import { Socket } from "net";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const bare = createBareServer("/bare/");
const rh = createRammerhead();
const failureFile = fs.readFileSync("Checkfailed.html", "utf8");
const LICENSE_SERVER_URL = "https://license.mercurywork.shop/validate?license="; const LICENSE_SERVER_URL = "https://license.mercurywork.shop/validate?license=";
const whiteListedDomains = ["nebulaproxy.io"]; // Add any public domains you have here
const failureFile = fs.readFileSync("Checkfailed.html", "utf8");
const rh = createRammerhead();
const rammerheadScopes = [ const rammerheadScopes = [
"/rammerhead.js", '/rammerhead.js',
"/hammerhead.js", '/hammerhead.js',
"/transport-worker.js", '/transport-worker.js',
"/task.js", '/task.js',
"/iframe-task.js", '/iframe-task.js',
"/worker-hammerhead.js", '/worker-hammerhead.js',
"/messaging", '/messaging',
"/sessionexists", '/sessionexists',
"/deletesession", '/deletesession',
"/newsession", '/newsession',
"/editsession", '/editsession',
"/needpassword", '/needpassword',
"/syncLocalStorage", '/syncLocalStorage',
"/api/shuffleDict", '/api/shuffleDict',
"/mainport"
]; ];
const rammerheadSession = /^\/[a-z0-9]{32}/; const rammerheadSession = /^\/[a-z0-9]{32}/;
console.log(`${chalk.magentaBright('Starting Nebula...')}\n`);
const app = express();
app.use(cookieParser())
// Congratulations! Masqr failed to validate, this is either your first visit or you're a FRAUD
async function MasqFail(req, res) {
if (!req.headers.host) {
// no bitch still using HTTP/1.0 go away
return;
}
const unsafeSuffix = req.headers.host + ".html"
let safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, '');
let safeJoin = path.join(process.cwd()+"/Masqrd", safeSuffix);
try {
await fs.promises.access(safeJoin) // man do I wish this was an if-then instead of a "exception on fail"
const failureFileLocal = await fs.promises.readFile(safeJoin, "utf8");
res.setHeader("Content-Type", "text/html");
res.send(failureFileLocal);
return;
} catch(e) {
res.setHeader("Content-Type", "text/html");
res.send(failureFile);
return;
}
}
// Woooooo masqr yayyyy (said no one)
// uncomment for masqr
/* app.use(async (req, res, next) => {
if (req.headers.host && whiteListedDomains.includes(req.headers.host)) {
next();
return;
}
if (req.url.includes("/bare/")) { // replace this with your bare endpoint
next();
return;
// Bypass for UV and other bares
}
const authheader = req.headers.authorization;
if (req.cookies["authcheck"]) {
next();
return;
}
if (req.cookies['refreshcheck'] != "true") {
res.cookie("refreshcheck", "true", {maxAge: 10000}) // 10s refresh check
MasqFail(req, res)
return;
}
if (!authheader) {
res.setHeader('WWW-Authenticate', 'Basic'); // Yeah so we need to do this to get the auth params, kinda annoying and just showing a login prompt gives it away so its behind a 10s refresh check
res.status(401);
MasqFail(req, res)
return;
}
const auth = Buffer.from(authheader.split(' ')[1],
'base64').toString().split(':');
const user = auth[0];
const pass = auth[1];
const licenseCheck = ((await (await fetch(LICENSE_SERVER_URL + pass + "&host=" + req.headers.host)).json()))["status"]
console.log(LICENSE_SERVER_URL + pass + "&host=" + req.headers.host +" returned " +licenseCheck)
if (licenseCheck == "License valid") {
res.cookie("authcheck", "true", {expires: new Date((Date.now()) + (365*24*60*60 * 1000))}) // authorize session, for like a year, by then the link will be expired lol
res.send(`<script> window.location.href = window.location.href </script>`) // fun hack to make the browser refresh and remove the auth params from the URL
return;
}
MasqFail(req, res)
return;
}) */
app.use(express.static("dist"));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
const server = createServer();
const bare = createBareServer('/bare/');
server.on('request', (req, res) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else if (shouldRouteRh(req)) {
routeRhRequest(req, res);
} else {
app(req, res);
}
});
server.on('upgrade', (req, socket, head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
} else {
wisp.routeRequest(req, socket as Socket, head);
}
});
function shouldRouteRh(req) { function shouldRouteRh(req) {
const url = new URL(req.url, "http://0.0.0.0"); const url = new URL(req.url, 'http://0.0.0.0');
return ( return (
rammerheadScopes.includes(url.pathname) || rammerheadScopes.includes(url.pathname) ||
rammerheadSession.test(url.pathname) rammerheadSession.test(url.pathname)
); );
} }
function routeRhRequest(req, res) { function routeRhRequest(req, res) {
rh.emit("request", req, res); rh.emit('request', req, res);
} }
function routeRhUpgrade(req, socket, head) { function routeRhUpgrade(req, socket, head) {
rh.emit("upgrade", req, socket, head); rh.emit('upgrade', req, socket, head);
} }
const serverFactory = (handler, opts) => { const port = parseInt(process.env.PORT || "8080");
return createServer()
.on("request", (req, res) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else if (shouldRouteRh(req)) {
routeRhRequest(req, res);
} else {
handler(req, res);
}
})
.on("upgrade", (req, socket, head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
} else {
wisp.routeRequest(req, socket as Socket, head);
}
});
};
const app = fastify({ logger: true, serverFactory }); server.listen(port, () => {
console.log(`${chalk.magentaBright("You can now use Nebula on port ") + chalk.bold(port)}\n`);
app.register(cookieParser);
await app.register(import("@fastify/compress"));
// Uncomment if you wish to add masqr.
/*
app.addHook("preHandler", async (req, reply) => {
if (req.cookies["authcheck"]) {
return reply;
}
const authheader = req.headers.authorization;
if (req.cookies["refreshcheck"] != "true") {
reply
.setCookie("refreshcheck", "true", { maxAge: 10000 })
.type("text/html")
.send(failureFile);
return reply;
}
if (!authheader) {
reply
.code(401)
.header("WWW-Authenticate", "Basic")
.type("text/html")
.send(failureFile);
return reply;
}
const auth = Buffer.from(authheader.split(" ")[1], "base64")
.toString()
.split(":");
const user = auth[0];
const pass = auth[1];
const licenseCheck = (
await (
await fetch(`${LICENSE_SERVER_URL}${pass}&host=${req.headers.host}`)
).json()
)["status"];
console.log(
`${LICENSE_SERVER_URL}${pass}&host=${req.headers.host} returned ${licenseCheck}`
);
if (licenseCheck === "License valid") {
reply.setCookie("authcheck", "true");
return reply;
}
reply.type("text/html").send(failureFile);
return reply;
}); */
app.register(fastifyStatic, {
root: path.join(__dirname, "dist"),
prefix: "/",
serve: true,
wildcard: false
});
app.get("/search=:query", async (req, res) => {
const { query } = req.params as { query: string }; // Define the type for req.params
const response = await fetch(
`http://api.duckduckgo.com/ac?q=${query}&format=json`
).then((apiRes) => apiRes.json());
res.send(response);
});
app.setNotFoundHandler((req, res) => {
res.sendFile("index.html"); // SPA catch-all
});
app.listen({
port: 8080
}); });