introduce a new instance api rate limiter

This commit is contained in:
lesion 2023-01-26 23:30:53 +01:00
parent c54630a2f4
commit a9c9fd38a5
No known key found for this signature in database
GPG key ID: 352918250B012177
5 changed files with 47 additions and 22 deletions

View file

@ -689,7 +689,7 @@ const eventController = {
...pagination,
replacements
}).catch(e => {
log.error('[EVENT]', e)
log.error('[EVENT]' + String(e))
return []
})

View file

@ -1,4 +1,3 @@
const rateLimit = require('express-rate-limit');
const log = require('../../log')
const nominatim = require('../../services/geocoding/nominatim')
const photon = require('../../services/geocoding/photon')
@ -8,15 +7,6 @@ let d = 0 // departure time
let h = 0 // hit geocoding provider time (aka Latency)
const geocodingController = {
/**
* TODO: replace/merge with a general 'instance rate-limiter' or 'instance api-related rate-limiter' when this will be defined
*/
instanceApiRateLimiter: rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
}),
/**
* Limit provider api usage.

View file

@ -3,8 +3,6 @@ const { Place, Event } = require('../models/models')
const eventController = require('./event')
const exportController = require('./export')
const { version } = require('../../../package.json')
const log = require('../../log')
const { Op, where, col, fn, cast } = require('sequelize')

View file

@ -20,6 +20,7 @@ const oauthController = require('./controller/oauth')
const announceController = require('./controller/announce')
const pluginController = require('./controller/plugins')
const geocodingController = require('./controller/geocoding')
const { DDOSProtectionApiRateLimiter, SPAMProtectionApiRateLimiter } = require('./limiter')
const helpers = require('../helpers')
const storage = require('./storage')
@ -30,6 +31,10 @@ module.exports = () => {
api.use(express.urlencoded({ extended: false }))
api.use(express.json())
if (process.env.NODE_ENV !== 'test') {
api.use(DDOSProtectionApiRateLimiter)
}
if (config.status !== 'READY') {
@ -66,12 +71,12 @@ module.exports = () => {
api.get('/ping', (_req, res) => res.sendStatus(200))
api.get('/user', isAuth, (req, res) => res.json(req.user))
api.post('/user/recover', userController.forgotPassword)
api.post('/user/recover', SPAMProtectionApiRateLimiter, userController.forgotPassword)
api.post('/user/check_recover_code', userController.checkRecoverCode)
api.post('/user/recover_password', userController.updatePasswordWithRecoverCode)
api.post('/user/recover_password', SPAMProtectionApiRateLimiter, userController.updatePasswordWithRecoverCode)
// register and add users
api.post('/user/register', userController.register)
api.post('/user/register', SPAMProtectionApiRateLimiter, userController.register)
api.post('/user', isAdmin, userController.create)
// update user
@ -127,7 +132,7 @@ module.exports = () => {
*/
// allow anyone to add an event (anon event has to be confirmed, TODO: flood protection)
api.post('/event', eventController.isAnonEventAllowed, upload.single('image'), eventController.add)
api.post('/event', eventController.isAnonEventAllowed, SPAMProtectionApiRateLimiter, upload.single('image'), eventController.add)
// api.get('/event/search', eventController.search)
@ -141,8 +146,8 @@ module.exports = () => {
api.get('/event/meta', eventController.searchMeta)
// add event notification TODO
api.post('/event/notification', eventController.addNotification)
api.delete('/event/notification/:code', eventController.delNotification)
// api.post('/event/notification', eventController.addNotification)
// api.delete('/event/notification/:code', eventController.delNotification)
api.post('/settings', isAdmin, settingsController.setRequest)
api.get('/settings', isAdmin, settingsController.getAll)
@ -173,8 +178,8 @@ module.exports = () => {
api.put('/place', isAdmin, placeController.updatePlace)
// - GEOCODING
api.get('/placeOSM/Nominatim/:place_details', helpers.isGeocodingEnabled, geocodingController.instanceApiRateLimiter, geocodingController.nominatimRateLimit, geocodingController._nominatim)
api.get('/placeOSM/Photon/:place_details', helpers.isGeocodingEnabled, geocodingController.instanceApiRateLimiter, geocodingController.photonRateLimit, geocodingController._photon)
api.get('/placeOSM/Nominatim/:place_details', helpers.isGeocodingEnabled, geocodingController.nominatimRateLimit, geocodingController._nominatim)
api.get('/placeOSM/Photon/:place_details', helpers.isGeocodingEnabled, geocodingController.photonRateLimit, geocodingController._photon)
// - TAGS
api.get('/tags', isAdmin, tagController.getAll)
@ -215,7 +220,7 @@ module.exports = () => {
// OAUTH
api.get('/clients', isAuth, oauthController.getClients)
api.get('/client/:client_id', isAuth, oauthController.getClient)
api.post('/client', oauthController.createClient)
api.post('/client', SPAMProtectionApiRateLimiter, oauthController.createClient)
}
api.use((_req, res) => res.sendStatus(404))

32
server/api/limiter.js Normal file
View file

@ -0,0 +1,32 @@
const rateLimit = require('express-rate-limit')
const log = require('../log')
const next = (req, res, next) => next()
const instanceApiRateLimiter = {
DDOSProtectionApiRateLimiter: (process.env.NODE_ENV === 'test' ? next : rateLimit({
windowMs: 60 * 1000, // 5 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 5 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
handler: (request, response, next, options) => {
log.warn(`DDOS protection api rate limiter: > 100req/minute/ip ${request.ip}`)
return response.status(options.statusCode).send(options.message)
}
})),
SPAMProtectionApiRateLimiter: (process.env.NODE_ENV === 'test' ? next : rateLimit({
windowMs: 5 * 60 * 1000, // 10 minutes
max: 3, // Limit each IP to 3 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
handler: (request, response, next, options) => {
log.warn(`SPAM protection api rate limiter: 3req/5min/ip ${request.ip}`)
return response.status(options.statusCode).send(options.message)
}
}))
}
module.exports = instanceApiRateLimiter