feat: Modify logger to log to file that supports fail2ban (#284)
parent
21ef850913
commit
6429e22d59
@ -1,5 +1,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
||||||
|
/logs
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# db
|
||||||
node db/init.js
|
node db/init.js
|
||||||
exec node app.js --prod $@
|
|
||||||
|
# app
|
||||||
|
export NODE_ENV=production
|
||||||
|
exec node app.js
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,78 @@
|
|||||||
|
const { expect } = require('chai');
|
||||||
|
const { getRemoteAddress } = require('../../utils/remoteAddress');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fake HTTP request object
|
||||||
|
* given to all api controllers.
|
||||||
|
*/
|
||||||
|
const MOCK_REQUEST = {
|
||||||
|
ip: '',
|
||||||
|
ips: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks the `MOCK_REQUEST` value.
|
||||||
|
* Should be called before asserting `getRemoteAddress`.
|
||||||
|
* @param {string} ip Mock remote IP address
|
||||||
|
* @param {any[]} ips Mock array of proxy IP addresses
|
||||||
|
*/
|
||||||
|
const mockRequest = (ip, ips) => {
|
||||||
|
MOCK_REQUEST.ip = ip;
|
||||||
|
MOCK_REQUEST.ips = ips;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks the `TRUST_PROXY` environment variable passed through `docker-compose` file.
|
||||||
|
* @param {boolean} trustProxy Whether the TRUST_PROXY environment variable was enabled.
|
||||||
|
*/
|
||||||
|
const mockProxyFlag = (trustProxy) => {
|
||||||
|
process.env.TRUST_PROXY = !!trustProxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('remoteAddress', () => {
|
||||||
|
describe('#getRemoteAddress(Request)', () => {
|
||||||
|
it('should get IPv4 remote address while not behind proxy and TRUST_PROXY=false', async () => {
|
||||||
|
const expectedAddress = '172.2.109.132';
|
||||||
|
|
||||||
|
mockRequest(`::ffff:${expectedAddress}`, null);
|
||||||
|
mockProxyFlag(false);
|
||||||
|
|
||||||
|
expect(getRemoteAddress(MOCK_REQUEST)).to.be.equal(expectedAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get IPv6 remote address while not behind proxy and TRUST_PROXY=false', async () => {
|
||||||
|
const expectedAddress = 'f53f:5832:9f1c:fe38:ce3d:1be8:81a2:115e';
|
||||||
|
|
||||||
|
mockRequest(expectedAddress, null);
|
||||||
|
mockProxyFlag(false);
|
||||||
|
|
||||||
|
expect(getRemoteAddress(MOCK_REQUEST)).to.be.equal(expectedAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get IPv4 remote address while behind proxy and TRUST_PROXY=true', async () => {
|
||||||
|
const expectedAddress = '172.2.109.132';
|
||||||
|
|
||||||
|
mockRequest(`::ffff:${expectedAddress}`, [
|
||||||
|
`::ffff:${expectedAddress}`,
|
||||||
|
'::ffff:192.182.23.111',
|
||||||
|
'::ffff:120.210.132.14',
|
||||||
|
]);
|
||||||
|
mockProxyFlag(true);
|
||||||
|
|
||||||
|
expect(getRemoteAddress(MOCK_REQUEST)).to.be.equal(expectedAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get IPv6 remote address while behind proxy and TRUST_PROXY=true', async () => {
|
||||||
|
const expectedAddress = 'f53f:5832:9f1c:fe38:ce3d:1be8:81a2:115e';
|
||||||
|
|
||||||
|
mockRequest(expectedAddress, [
|
||||||
|
expectedAddress,
|
||||||
|
'9d74:fb18:3b95:801f:8751:8d18:8207:b322',
|
||||||
|
'598e:4291:e1b3:2991:5d17:00af:1b6b:802c',
|
||||||
|
]);
|
||||||
|
mockProxyFlag(true);
|
||||||
|
|
||||||
|
expect(getRemoteAddress(MOCK_REQUEST)).to.be.equal(expectedAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
const winston = require('winston');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default timestamp used by the logger.
|
||||||
|
* Format example: "2022-08-18 6:30:02"
|
||||||
|
*/
|
||||||
|
const defaultLogTimestampFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
|
const logfile = `${process.cwd()}/logs/planka.log`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log level for both console and file log sinks.
|
||||||
|
*
|
||||||
|
* Refer {@link https://github.com/winstonjs/winston#logging here}
|
||||||
|
* for more information on Winston log levels.
|
||||||
|
*/
|
||||||
|
const logLevel = process.env.NODE_ENV === 'production' ? 'info' : 'debug';
|
||||||
|
|
||||||
|
const logFormat = winston.format.combine(
|
||||||
|
winston.format.uncolorize(),
|
||||||
|
winston.format.timestamp({ format: defaultLogTimestampFormat }),
|
||||||
|
winston.format.printf((log) => `${log.timestamp} [${log.level[0].toUpperCase()}] ${log.message}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
const customLogger = new winston.createLogger({
|
||||||
|
transports: [
|
||||||
|
new winston.transports.File({
|
||||||
|
level: logLevel,
|
||||||
|
format: logFormat,
|
||||||
|
filename: logfile,
|
||||||
|
}),
|
||||||
|
new winston.transports.Console({
|
||||||
|
level: logLevel,
|
||||||
|
format: logFormat,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
customLogger,
|
||||||
|
};
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* The IP address of the client that just made a request to this application, whether
|
||||||
|
* or not the TRUST_PROXY env variable is true and if endpoint accessed through a proxy.
|
||||||
|
* @param {Request} request The endpoint Request object
|
||||||
|
* @returns The IP address of the client that just made a request
|
||||||
|
*/
|
||||||
|
const getRemoteAddress = (request) => {
|
||||||
|
let remoteAddress = request.ip;
|
||||||
|
|
||||||
|
// Assert if "X-Forwarded-For" header contains any addresses
|
||||||
|
if (process.env.TRUST_PROXY && !_.isEmpty(request.ips)) {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
remoteAddress = request.ips[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert address from IPV6 to IPV4 if client device is IPV4.
|
||||||
|
const defaultIPV6Regex = /^::ffff:((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/g;
|
||||||
|
if (remoteAddress.match(defaultIPV6Regex)) {
|
||||||
|
remoteAddress = remoteAddress.replace('::ffff:', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getRemoteAddress,
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue