Merge pull request #1827 from theS1LV3R/master

feat: get client ip from x-forwarded-for header if available
This commit is contained in:
Louis Lam 2022-07-31 23:43:39 +08:00 committed by GitHub
commit 6e29feffd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 19 deletions

View file

@ -254,7 +254,9 @@ let needSetup = false;
// *************************** // ***************************
socket.on("loginByToken", async (token, callback) => { socket.on("loginByToken", async (token, callback) => {
log.info("auth", `Login by token. IP=${getClientIp(socket)}`); const clientIP = await server.getClientIP(socket);
log.info("auth", `Login by token. IP=${clientIP}`);
try { try {
let decoded = jwt.verify(token, jwtSecret); let decoded = jwt.verify(token, jwtSecret);
@ -270,14 +272,14 @@ let needSetup = false;
afterLogin(socket, user); afterLogin(socket, user);
log.debug("auth", "afterLogin ok"); log.debug("auth", "afterLogin ok");
log.info("auth", `Successfully logged in user ${decoded.username}. IP=${getClientIp(socket)}`); log.info("auth", `Successfully logged in user ${decoded.username}. IP=${clientIP}`);
callback({ callback({
ok: true, ok: true,
}); });
} else { } else {
log.info("auth", `Inactive or deleted user ${decoded.username}. IP=${getClientIp(socket)}`); log.info("auth", `Inactive or deleted user ${decoded.username}. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -286,7 +288,7 @@ let needSetup = false;
} }
} catch (error) { } catch (error) {
log.error("auth", `Invalid token. IP=${getClientIp(socket)}`); log.error("auth", `Invalid token. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -297,7 +299,9 @@ let needSetup = false;
}); });
socket.on("login", async (data, callback) => { socket.on("login", async (data, callback) => {
log.info("auth", `Login by username + password. IP=${getClientIp(socket)}`); const clientIP = await server.getClientIP(socket);
log.info("auth", `Login by username + password. IP=${clientIP}`);
// Checking // Checking
if (typeof callback !== "function") { if (typeof callback !== "function") {
@ -310,7 +314,7 @@ let needSetup = false;
// Login Rate Limit // Login Rate Limit
if (! await loginRateLimiter.pass(callback)) { if (! await loginRateLimiter.pass(callback)) {
log.info("auth", `Too many failed requests for user ${data.username}. IP=${getClientIp(socket)}`); log.info("auth", `Too many failed requests for user ${data.username}. IP=${clientIP}`);
return; return;
} }
@ -320,7 +324,7 @@ let needSetup = false;
if (user.twofa_status === 0) { if (user.twofa_status === 0) {
afterLogin(socket, user); afterLogin(socket, user);
log.info("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); log.info("auth", `Successfully logged in user ${data.username}. IP=${clientIP}`);
callback({ callback({
ok: true, ok: true,
@ -332,7 +336,7 @@ let needSetup = false;
if (user.twofa_status === 1 && !data.token) { if (user.twofa_status === 1 && !data.token) {
log.info("auth", `2FA token required for user ${data.username}. IP=${getClientIp(socket)}`); log.info("auth", `2FA token required for user ${data.username}. IP=${clientIP}`);
callback({ callback({
tokenRequired: true, tokenRequired: true,
@ -350,7 +354,7 @@ let needSetup = false;
socket.userID, socket.userID,
]); ]);
log.info("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); log.info("auth", `Successfully logged in user ${data.username}. IP=${clientIP}`);
callback({ callback({
ok: true, ok: true,
@ -360,7 +364,7 @@ let needSetup = false;
}); });
} else { } else {
log.warn("auth", `Invalid token provided for user ${data.username}. IP=${getClientIp(socket)}`); log.warn("auth", `Invalid token provided for user ${data.username}. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -370,7 +374,7 @@ let needSetup = false;
} }
} else { } else {
log.warn("auth", `Incorrect username or password for user ${data.username}. IP=${getClientIp(socket)}`); log.warn("auth", `Incorrect username or password for user ${data.username}. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -442,6 +446,8 @@ let needSetup = false;
}); });
socket.on("save2FA", async (currentPassword, callback) => { socket.on("save2FA", async (currentPassword, callback) => {
const clientIP = await server.getClientIP(socket);
try { try {
if (! await twoFaRateLimiter.pass(callback)) { if (! await twoFaRateLimiter.pass(callback)) {
return; return;
@ -454,7 +460,7 @@ let needSetup = false;
socket.userID, socket.userID,
]); ]);
log.info("auth", `Saved 2FA token. IP=${getClientIp(socket)}`); log.info("auth", `Saved 2FA token. IP=${clientIP}`);
callback({ callback({
ok: true, ok: true,
@ -462,7 +468,7 @@ let needSetup = false;
}); });
} catch (error) { } catch (error) {
log.error("auth", `Error changing 2FA token. IP=${getClientIp(socket)}`); log.error("auth", `Error changing 2FA token. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -472,6 +478,8 @@ let needSetup = false;
}); });
socket.on("disable2FA", async (currentPassword, callback) => { socket.on("disable2FA", async (currentPassword, callback) => {
const clientIP = await server.getClientIP(socket);
try { try {
if (! await twoFaRateLimiter.pass(callback)) { if (! await twoFaRateLimiter.pass(callback)) {
return; return;
@ -481,7 +489,7 @@ let needSetup = false;
await doubleCheckPassword(socket, currentPassword); await doubleCheckPassword(socket, currentPassword);
await TwoFA.disable2FA(socket.userID); await TwoFA.disable2FA(socket.userID);
log.info("auth", `Disabled 2FA token. IP=${getClientIp(socket)}`); log.info("auth", `Disabled 2FA token. IP=${clientIP}`);
callback({ callback({
ok: true, ok: true,
@ -489,7 +497,7 @@ let needSetup = false;
}); });
} catch (error) { } catch (error) {
log.error("auth", `Error disabling 2FA token. IP=${getClientIp(socket)}`); log.error("auth", `Error disabling 2FA token. IP=${clientIP}`);
callback({ callback({
ok: false, ok: false,
@ -1684,10 +1692,6 @@ async function shutdownFunction(signal) {
await cloudflaredStop(); await cloudflaredStop();
} }
function getClientIp(socket) {
return socket.client.conn.remoteAddress.replace(/^.*:/, "");
}
/** Final function called before application exits */ /** Final function called before application exits */
function finalFunction() { function finalFunction() {
log.info("server", "Graceful shutdown successful!"); log.info("server", "Graceful shutdown successful!");

View file

@ -8,6 +8,7 @@ const { log } = require("../src/util");
const Database = require("./database"); const Database = require("./database");
const util = require("util"); const util = require("util");
const { CacheableDnsHttpAgent } = require("./cacheable-dns-http-agent"); const { CacheableDnsHttpAgent } = require("./cacheable-dns-http-agent");
const { Settings } = require("./settings");
/** /**
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
@ -128,6 +129,18 @@ class UptimeKumaServer {
errorLogStream.end(); errorLogStream.end();
} }
async getClientIP(socket) {
const clientIP = socket.client.conn.remoteAddress.replace(/^.*:/, "");
if (await Settings.get("trustProxy")) {
return socket.client.conn.request.headers["x-forwarded-for"]
|| socket.client.conn.request.headers["x-real-ip"]
|| clientIP;
} else {
return clientIP;
}
}
} }
module.exports = { module.exports = {

View file

@ -289,6 +289,7 @@ exports.postgresQuery = function (connectionString, query) {
* Retrieve value of setting based on key * Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve * @param {string} key Key of setting to retrieve
* @returns {Promise<any>} Value * @returns {Promise<any>} Value
* @deprecated Use await Settings.get(key)
*/ */
exports.setting = async function (key) { exports.setting = async function (key) {
return await Settings.get(key); return await Settings.get(key);