mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 13:46:13 +08:00 
			
		
		
		
	Smoothing the update for origin check (#4216)
This commit is contained in:
		@@ -78,7 +78,6 @@ function disconnectAllSocketClients(username, password) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Disconnect all socket connections
 | 
					        // Disconnect all socket connections
 | 
				
			||||||
        const socket = io(localWebSocketURL, {
 | 
					        const socket = io(localWebSocketURL, {
 | 
				
			||||||
            transports: [ "websocket" ],
 | 
					 | 
				
			||||||
            reconnection: false,
 | 
					            reconnection: false,
 | 
				
			||||||
            timeout: 5000,
 | 
					            timeout: 5000,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,10 @@ if (!process.env.UPTIME_KUMA_WS_ORIGIN_CHECK) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
log.info("server", "Node Env: " + process.env.NODE_ENV);
 | 
					log.info("server", "Node Env: " + process.env.NODE_ENV);
 | 
				
			||||||
log.info("server", "Inside Container: " + (process.env.UPTIME_KUMA_IS_CONTAINER === "1"));
 | 
					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");
 | 
					log.info("server", "Importing Node libraries");
 | 
				
			||||||
const fs = require("fs");
 | 
					const fs = require("fs");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,39 +99,63 @@ class UptimeKumaServer {
 | 
				
			|||||||
        UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
 | 
					        UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
 | 
				
			||||||
        UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
 | 
					        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, {
 | 
					        this.io = new Server(this.httpServer, {
 | 
				
			||||||
            allowRequest: (req, callback) => {
 | 
					            cors,
 | 
				
			||||||
                let isOriginValid = true;
 | 
					            allowRequest: async (req, callback) => {
 | 
				
			||||||
                const bypass = isDev || process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass";
 | 
					                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) {
 | 
					                const clientIP = await this.getClientIPwithProxy(req.connection.remoteAddress, req.headers);
 | 
				
			||||||
                    let host = req.headers.host;
 | 
					                log.info("socket", `New ${transport} connection, IP = ${clientIP}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // If this is set, it means the request is from the browser
 | 
					                // The following check is only for websocket connections, polling connections are already protected by CORS
 | 
				
			||||||
                    let origin = req.headers.origin;
 | 
					                if (transport === "polling") {
 | 
				
			||||||
 | 
					                    callback(null, true);
 | 
				
			||||||
                    // If this is from the browser, check if the origin is allowed
 | 
					                } else if (transport === "websocket") {
 | 
				
			||||||
                    if (origin) {
 | 
					                    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 {
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
 | 
					                            let host = req.headers.host;
 | 
				
			||||||
 | 
					                            let origin = req.headers.origin;
 | 
				
			||||||
                            let originURL = new URL(origin);
 | 
					                            let originURL = new URL(origin);
 | 
				
			||||||
 | 
					                            let xForwardedFor;
 | 
				
			||||||
 | 
					                            if (await Settings.get("trustProxy")) {
 | 
				
			||||||
 | 
					                                xForwardedFor = req.headers["x-forwarded-for"];
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            if (host !== originURL.host) {
 | 
					                            if (host !== originURL.host && xForwardedFor !== originURL.host) {
 | 
				
			||||||
                                isOriginValid = false;
 | 
					                                callback(null, false);
 | 
				
			||||||
                                log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${req.socket.remoteAddress}`);
 | 
					                                log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${clientIP}`);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                callback(null, true);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        } catch (e) {
 | 
					                        } catch (e) {
 | 
				
			||||||
                            // Invalid origin url, probably not from browser
 | 
					                            // Invalid origin url, probably not from browser
 | 
				
			||||||
                            isOriginValid = false;
 | 
					                            callback(null, false);
 | 
				
			||||||
                            log.error("auth", `Invalid origin url (${origin}), IP: ${req.socket.remoteAddress}`);
 | 
					                            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 +292,28 @@ class UptimeKumaServer {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get the IP of the client connected to the socket
 | 
					     * Get the IP of the client connected to the socket
 | 
				
			||||||
     * @param {Socket} socket
 | 
					     * @param {Socket} socket
 | 
				
			||||||
     * @returns {string}
 | 
					     * @returns {Promise<string>}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async getClientIP(socket) {
 | 
					    getClientIP(socket) {
 | 
				
			||||||
        let clientIP = socket.client.conn.remoteAddress;
 | 
					        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) {
 | 
					        if (clientIP === undefined) {
 | 
				
			||||||
            clientIP = "";
 | 
					            clientIP = "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (await Settings.get("trustProxy")) {
 | 
					        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)
 | 
					            return (typeof forwardedFor === "string" ? forwardedFor.split(",")[0].trim() : null)
 | 
				
			||||||
                || socket.client.conn.request.headers["x-real-ip"]
 | 
					                || headers["x-real-ip"]
 | 
				
			||||||
                || clientIP.replace(/^::ffff:/, "");
 | 
					                || clientIP.replace(/^::ffff:/, "");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return clientIP.replace(/^::ffff:/, "");
 | 
					            return clientIP.replace(/^::ffff:/, "");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,21 +91,20 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            this.socket.initedSocketIO = true;
 | 
					            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";
 | 
					            const env = process.env.NODE_ENV || "production";
 | 
				
			||||||
            if (env === "development" && isDevContainer()) {
 | 
					            if (env === "development" && isDevContainer()) {
 | 
				
			||||||
                wsHost = protocol + getDevContainerServerHostname();
 | 
					                url = protocol + getDevContainerServerHostname();
 | 
				
			||||||
            } else if (env === "development" || localStorage.dev === "dev") {
 | 
					            } else if (env === "development" || localStorage.dev === "dev") {
 | 
				
			||||||
                wsHost = protocol + location.hostname + ":3001";
 | 
					                url = protocol + location.hostname + ":3001";
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                wsHost = protocol + location.host;
 | 
					                // Connect to the current url
 | 
				
			||||||
 | 
					                url = undefined;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            socket = io(wsHost, {
 | 
					            socket = io(url);
 | 
				
			||||||
                transports: [ "websocket" ],
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            socket.on("info", (info) => {
 | 
					            socket.on("info", (info) => {
 | 
				
			||||||
                this.info = info;
 | 
					                this.info = info;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user