mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-09-13 15:06:59 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f1e2ee74ea | ||
|
8d847abf35 | ||
|
8151ac0e25 | ||
|
4185ec20b0 | ||
|
4245ea86e7 | ||
|
f861a48dfc | ||
|
fa1214ae5e |
@@ -78,7 +78,6 @@ function disconnectAllSocketClients(username, password) {
|
||||
|
||||
// Disconnect all socket connections
|
||||
const socket = io(localWebSocketURL, {
|
||||
transports: [ "websocket" ],
|
||||
reconnection: false,
|
||||
timeout: 5000,
|
||||
});
|
||||
|
759
package-lock.json
generated
759
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"version": "1.23.9",
|
||||
"version": "1.23.11",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -42,7 +42,7 @@
|
||||
"build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
|
||||
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push",
|
||||
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||
"setup": "git checkout 1.23.9 && npm ci --production && npm run download-dist",
|
||||
"setup": "git checkout 1.23.11 && npm ci --production && npm run download-dist",
|
||||
"download-dist": "node extra/download-dist.js",
|
||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||
"reset-password": "node extra/reset-password.js",
|
||||
|
@@ -39,7 +39,8 @@ class TailscalePing extends MonitorType {
|
||||
async runTailscalePing(hostname, interval) {
|
||||
let timeout = interval * 1000 * 0.8;
|
||||
let res = await childProcessAsync.spawn("tailscale", [ "ping", "--c", "1", hostname ], {
|
||||
timeout: timeout
|
||||
timeout: timeout,
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (res.stderr && res.stderr.toString()) {
|
||||
throw new Error(`Error in output: ${res.stderr.toString()}`);
|
||||
|
@@ -11,7 +11,9 @@ class Apprise extends NotificationProvider {
|
||||
args.push("-t");
|
||||
args.push(notification.title);
|
||||
}
|
||||
const s = await childProcessAsync.spawn("apprise", args);
|
||||
const s = await childProcessAsync.spawn("apprise", args, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
|
||||
|
||||
|
@@ -54,7 +54,10 @@ if (!process.env.UPTIME_KUMA_WS_ORIGIN_CHECK) {
|
||||
|
||||
log.info("server", "Node Env: " + process.env.NODE_ENV);
|
||||
log.info("server", "Inside Container: " + (process.env.UPTIME_KUMA_IS_CONTAINER === "1"));
|
||||
log.info("server", "WebSocket Origin Check: " + process.env.UPTIME_KUMA_WS_ORIGIN_CHECK);
|
||||
|
||||
if (process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass") {
|
||||
log.warn("server", "WebSocket Origin Check: " + process.env.UPTIME_KUMA_WS_ORIGIN_CHECK);
|
||||
}
|
||||
|
||||
log.info("server", "Importing Node libraries");
|
||||
const fs = require("fs");
|
||||
@@ -1151,7 +1154,7 @@ let needSetup = false;
|
||||
let user = await doubleCheckPassword(socket, password.currentPassword);
|
||||
await user.resetPassword(password.newPassword);
|
||||
|
||||
server.disconnectAllSocketClient(user.id, socket.id);
|
||||
server.disconnectAllSocketClients(user.id, socket.id);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
|
@@ -99,39 +99,64 @@ class UptimeKumaServer {
|
||||
UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
||||
|
||||
// Allow all CORS origins (polling) in development
|
||||
let cors = undefined;
|
||||
if (isDev) {
|
||||
cors = {
|
||||
origin: "*",
|
||||
};
|
||||
}
|
||||
|
||||
this.io = new Server(this.httpServer, {
|
||||
allowRequest: (req, callback) => {
|
||||
let isOriginValid = true;
|
||||
const bypass = isDev || process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass";
|
||||
cors,
|
||||
allowRequest: async (req, callback) => {
|
||||
let transport;
|
||||
// It should be always true, but just in case, because this property is not documented
|
||||
if (req._query) {
|
||||
transport = req._query.transport;
|
||||
} else {
|
||||
log.error("socket", "Ops!!! Cannot get transport type, assume that it is polling");
|
||||
transport = "polling";
|
||||
}
|
||||
|
||||
if (!bypass) {
|
||||
let host = req.headers.host;
|
||||
const clientIP = await this.getClientIPwithProxy(req.connection.remoteAddress, req.headers);
|
||||
log.info("socket", `New ${transport} connection, IP = ${clientIP}`);
|
||||
|
||||
// If this is set, it means the request is from the browser
|
||||
let origin = req.headers.origin;
|
||||
// The following check is only for websocket connections, polling connections are already protected by CORS
|
||||
if (transport === "polling") {
|
||||
callback(null, true);
|
||||
} else if (transport === "websocket") {
|
||||
const bypass = process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass";
|
||||
if (bypass) {
|
||||
log.info("auth", "WebSocket origin check is bypassed");
|
||||
callback(null, true);
|
||||
} else if (!req.headers.origin) {
|
||||
log.info("auth", "WebSocket with no origin is allowed");
|
||||
callback(null, true);
|
||||
} else {
|
||||
let host = req.headers.host;
|
||||
let origin = req.headers.origin;
|
||||
|
||||
// If this is from the browser, check if the origin is allowed
|
||||
if (origin) {
|
||||
try {
|
||||
let originURL = new URL(origin);
|
||||
let xForwardedFor;
|
||||
if (await Settings.get("trustProxy")) {
|
||||
xForwardedFor = req.headers["x-forwarded-for"];
|
||||
}
|
||||
|
||||
if (host !== originURL.host) {
|
||||
isOriginValid = false;
|
||||
log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${req.socket.remoteAddress}`);
|
||||
if (host !== originURL.host && xForwardedFor !== originURL.host) {
|
||||
callback(null, false);
|
||||
log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${clientIP}`);
|
||||
} else {
|
||||
callback(null, true);
|
||||
}
|
||||
} catch (e) {
|
||||
// Invalid origin url, probably not from browser
|
||||
isOriginValid = false;
|
||||
log.error("auth", `Invalid origin url (${origin}), IP: ${req.socket.remoteAddress}`);
|
||||
callback(null, false);
|
||||
log.error("auth", `Invalid origin url (${origin}), IP: ${clientIP}`);
|
||||
}
|
||||
} else {
|
||||
log.info("auth", `Origin is not set, IP: ${req.socket.remoteAddress}`);
|
||||
}
|
||||
} else {
|
||||
log.debug("auth", "Origin check is bypassed");
|
||||
}
|
||||
|
||||
callback(null, isOriginValid);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -268,20 +293,28 @@ class UptimeKumaServer {
|
||||
/**
|
||||
* Get the IP of the client connected to the socket
|
||||
* @param {Socket} socket
|
||||
* @returns {string}
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getClientIP(socket) {
|
||||
let clientIP = socket.client.conn.remoteAddress;
|
||||
getClientIP(socket) {
|
||||
return this.getClientIPwithProxy(socket.client.conn.remoteAddress, socket.client.conn.request.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} clientIP
|
||||
* @param {IncomingHttpHeaders} headers
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getClientIPwithProxy(clientIP, headers) {
|
||||
if (clientIP === undefined) {
|
||||
clientIP = "";
|
||||
}
|
||||
|
||||
if (await Settings.get("trustProxy")) {
|
||||
const forwardedFor = socket.client.conn.request.headers["x-forwarded-for"];
|
||||
const forwardedFor = headers["x-forwarded-for"];
|
||||
|
||||
return (typeof forwardedFor === "string" ? forwardedFor.split(",")[0].trim() : null)
|
||||
|| socket.client.conn.request.headers["x-real-ip"]
|
||||
|| headers["x-real-ip"]
|
||||
|| clientIP.replace(/^::ffff:/, "");
|
||||
} else {
|
||||
return clientIP.replace(/^::ffff:/, "");
|
||||
|
@@ -91,21 +91,20 @@ export default {
|
||||
|
||||
this.socket.initedSocketIO = true;
|
||||
|
||||
let protocol = (location.protocol === "https:") ? "wss://" : "ws://";
|
||||
let protocol = location.protocol + "//";
|
||||
|
||||
let wsHost;
|
||||
let url;
|
||||
const env = process.env.NODE_ENV || "production";
|
||||
if (env === "development" && isDevContainer()) {
|
||||
wsHost = protocol + getDevContainerServerHostname();
|
||||
url = protocol + getDevContainerServerHostname();
|
||||
} else if (env === "development" || localStorage.dev === "dev") {
|
||||
wsHost = protocol + location.hostname + ":3001";
|
||||
url = protocol + location.hostname + ":3001";
|
||||
} else {
|
||||
wsHost = protocol + location.host;
|
||||
// Connect to the current url
|
||||
url = undefined;
|
||||
}
|
||||
|
||||
socket = io(wsHost, {
|
||||
transports: [ "websocket" ],
|
||||
});
|
||||
socket = io(url);
|
||||
|
||||
socket.on("info", (info) => {
|
||||
this.info = info;
|
||||
|
Reference in New Issue
Block a user