Add more rate limits

This commit is contained in:
Chocobozzz 2023-07-25 15:18:10 +02:00
parent 9901c8d690
commit 97583d0023
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
23 changed files with 216 additions and 37 deletions

View File

@ -36,6 +36,26 @@ rates_limit:
# 10 attempts in 10 min
window: 10 minutes
max: 10
plugins:
# 500 attempts in 10 seconds (we also serve plugin static files)
window: 10 seconds
max: 500
well_known:
# 200 attempts in 10 seconds
window: 10 seconds
max: 200
feeds:
# 50 attempts in 10 seconds
window: 10 seconds
max: 50
activity_pub:
# 500 attempts in 10 seconds (we can have many AP requests)
window: 10 seconds
max: 500
client: # HTML files generated by PeerTube
# 500 attempts in 10 seconds (to not break crawlers)
window: 10 seconds
max: 500
oauth2:
token_lifetime:

View File

@ -34,6 +34,26 @@ rates_limit:
# 10 attempts in 10 min
window: 10 minutes
max: 10
plugins:
# 500 attempts in 10 seconds (we also serve plugin static files)
window: 10 seconds
max: 500
well_known:
# 200 attempts in 10 seconds
window: 10 seconds
max: 200
feeds:
# 50 attempts in 10 seconds
window: 10 seconds
max: 50
activity_pub:
# 500 attempts in 10 seconds (we can have many AP requests)
window: 10 seconds
max: 500
client: # HTML files generated by PeerTube
# 500 attempts in 10 seconds (to not break crawlers)
window: 10 seconds
max: 500
oauth2:
token_lifetime:

View File

@ -115,7 +115,7 @@ import {
pluginsRouter,
trackerRouter,
createWebsocketTrackerServer,
botsRouter,
sitemapRouter,
downloadRouter
} from './server/controllers'
import { advertiseDoNotTrack } from './server/middlewares/dnt'
@ -222,9 +222,7 @@ OpenTelemetryMetrics.Instance.init(app)
// ----------- Views, routes and static files -----------
// API
const apiRoute = '/api/' + API_VERSION
app.use(apiRoute, apiRouter)
app.use('/api/' + API_VERSION, apiRouter)
// Services (oembed...)
app.use('/services', servicesRouter)
@ -235,7 +233,7 @@ app.use('/', pluginsRouter)
app.use('/', activityPubRouter)
app.use('/', feedsRouter)
app.use('/', trackerRouter)
app.use('/', botsRouter)
app.use('/', sitemapRouter)
// Static files
app.use('/', staticRouter)

View File

@ -19,6 +19,7 @@ import {
getLocalVideoSharesActivityPubUrl
} from '../../lib/activitypub/url'
import {
activityPubRateLimiter,
asyncMiddleware,
ensureIsLocalChannel,
executeIfActivityPub,
@ -47,32 +48,38 @@ activityPubClientRouter.use(cors())
activityPubClientRouter.get(
[ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ],
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(localAccountValidator),
asyncMiddleware(accountController)
)
activityPubClientRouter.get('/accounts?/:name/followers',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(localAccountValidator),
asyncMiddleware(accountFollowersController)
)
activityPubClientRouter.get('/accounts?/:name/following',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(localAccountValidator),
asyncMiddleware(accountFollowingController)
)
activityPubClientRouter.get('/accounts?/:name/playlists',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(localAccountValidator),
asyncMiddleware(accountPlaylistsController)
)
activityPubClientRouter.get('/accounts?/:name/likes/:videoId',
executeIfActivityPub,
activityPubRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
asyncMiddleware(getAccountVideoRateFactory('like'))
)
activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
executeIfActivityPub,
activityPubRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')),
asyncMiddleware(getAccountVideoRateFactory('dislike'))
@ -81,47 +88,56 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
activityPubClientRouter.get(
[ '/videos/watch/:id', '/w/:id' ],
executeIfActivityPub,
activityPubRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController)
)
activityPubClientRouter.get('/videos/watch/:id/activity',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController)
)
activityPubClientRouter.get('/videos/watch/:id/announces',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
asyncMiddleware(videoAnnouncesController)
)
activityPubClientRouter.get('/videos/watch/:id/announces/:actorId',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosShareValidator),
asyncMiddleware(videoAnnounceController)
)
activityPubClientRouter.get('/videos/watch/:id/likes',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
asyncMiddleware(videoLikesController)
)
activityPubClientRouter.get('/videos/watch/:id/dislikes',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
asyncMiddleware(videoDislikesController)
)
activityPubClientRouter.get('/videos/watch/:id/comments',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
asyncMiddleware(videoCommentsController)
)
activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoCommentGetValidator),
asyncMiddleware(videoCommentController)
)
activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoCommentGetValidator),
asyncMiddleware(videoCommentController)
)
@ -129,24 +145,28 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity
activityPubClientRouter.get(
[ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ],
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator),
ensureIsLocalChannel,
asyncMiddleware(videoChannelController)
)
activityPubClientRouter.get('/video-channels/:nameWithHost/followers',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator),
ensureIsLocalChannel,
asyncMiddleware(videoChannelFollowersController)
)
activityPubClientRouter.get('/video-channels/:nameWithHost/following',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator),
ensureIsLocalChannel,
asyncMiddleware(videoChannelFollowingController)
)
activityPubClientRouter.get('/video-channels/:nameWithHost/playlists',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator),
ensureIsLocalChannel,
asyncMiddleware(videoChannelPlaylistsController)
@ -154,11 +174,13 @@ activityPubClientRouter.get('/video-channels/:nameWithHost/playlists',
activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoFileRedundancyGetValidator),
asyncMiddleware(videoRedundancyController)
)
activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistType/:videoId',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoPlaylistRedundancyGetValidator),
asyncMiddleware(videoRedundancyController)
)
@ -166,17 +188,20 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT
activityPubClientRouter.get(
[ '/video-playlists/:playlistId', '/videos/watch/playlist/:playlistId', '/w/p/:playlistId' ],
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoPlaylistsGetValidator('all')),
asyncMiddleware(videoPlaylistController)
)
activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(videoPlaylistElementAPGetValidator),
asyncMiddleware(videoPlaylistElementController)
)
activityPubClientRouter.get('/videos/local-viewer/:localViewerId',
executeIfActivityPub,
activityPubRateLimiter,
asyncMiddleware(getVideoLocalViewerValidator),
asyncMiddleware(getVideoLocalViewerController)
)

View File

@ -5,6 +5,7 @@ import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
import { logger } from '../../helpers/logger'
import {
activityPubRateLimiter,
asyncMiddleware,
checkSignature,
ensureIsLocalChannel,
@ -17,6 +18,7 @@ import { activityPubValidator } from '../../middlewares/validators/activitypub/a
const inboxRouter = express.Router()
inboxRouter.post('/inbox',
activityPubRateLimiter,
signatureValidator,
asyncMiddleware(checkSignature),
asyncMiddleware(activityPubValidator),
@ -24,13 +26,16 @@ inboxRouter.post('/inbox',
)
inboxRouter.post('/accounts/:name/inbox',
activityPubRateLimiter,
signatureValidator,
asyncMiddleware(checkSignature),
asyncMiddleware(localAccountValidator),
asyncMiddleware(activityPubValidator),
inboxController
)
inboxRouter.post('/video-channels/:nameWithHost/inbox',
activityPubRateLimiter,
signatureValidator,
asyncMiddleware(checkSignature),
asyncMiddleware(videoChannelsNameWithHostValidator),

View File

@ -1,4 +1,5 @@
import express from 'express'
import { activityPubClientRouter } from './client'
import { inboxRouter } from './inbox'
import { outboxRouter } from './outbox'

View File

@ -7,7 +7,13 @@ import { VideoPrivacy } from '../../../shared/models/videos'
import { logger } from '../../helpers/logger'
import { buildAudience } from '../../lib/activitypub/audience'
import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
import { asyncMiddleware, ensureIsLocalChannel, localAccountValidator, videoChannelsNameWithHostValidator } from '../../middlewares'
import {
activityPubRateLimiter,
asyncMiddleware,
ensureIsLocalChannel,
localAccountValidator,
videoChannelsNameWithHostValidator
} from '../../middlewares'
import { apPaginationValidator } from '../../middlewares/validators/activitypub'
import { VideoModel } from '../../models/video/video'
import { activityPubResponse } from './utils'
@ -15,12 +21,14 @@ import { activityPubResponse } from './utils'
const outboxRouter = express.Router()
outboxRouter.get('/accounts/:name/outbox',
activityPubRateLimiter,
apPaginationValidator,
localAccountValidator,
asyncMiddleware(outboxController)
)
outboxRouter.get('/video-channels/:nameWithHost/outbox',
activityPubRateLimiter,
apPaginationValidator,
asyncMiddleware(videoChannelsNameWithHostValidator),
ensureIsLocalChannel,

View File

@ -5,27 +5,53 @@ import { join } from 'path'
import { logger } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { Hooks } from '@server/lib/plugins/hooks'
import { root } from '@shared/core-utils'
import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
import { HttpStatusCode } from '@shared/models'
import { root } from '@shared/core-utils'
import { STATIC_MAX_AGE } from '../initializers/constants'
import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
import { asyncMiddleware, embedCSP } from '../middlewares'
import { asyncMiddleware, buildRateLimiter, embedCSP } from '../middlewares'
const clientsRouter = express.Router()
const clientsRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.CLIENT.WINDOW_MS,
max: CONFIG.RATES_LIMIT.CLIENT.MAX
})
const distPath = join(root(), 'client', 'dist')
const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html')
// Special route that add OpenGraph and oEmbed tags
// Do not use a template engine for a so little thing
clientsRouter.use([ '/w/p/:id', '/videos/watch/playlist/:id' ], asyncMiddleware(generateWatchPlaylistHtmlPage))
clientsRouter.use([ '/w/:id', '/videos/watch/:id' ], asyncMiddleware(generateWatchHtmlPage))
clientsRouter.use([ '/accounts/:nameWithHost', '/a/:nameWithHost' ], asyncMiddleware(generateAccountHtmlPage))
clientsRouter.use([ '/video-channels/:nameWithHost', '/c/:nameWithHost' ], asyncMiddleware(generateVideoChannelHtmlPage))
clientsRouter.use('/@:nameWithHost', asyncMiddleware(generateActorHtmlPage))
clientsRouter.use([ '/w/p/:id', '/videos/watch/playlist/:id' ],
clientsRateLimiter,
asyncMiddleware(generateWatchPlaylistHtmlPage)
)
clientsRouter.use([ '/w/:id', '/videos/watch/:id' ],
clientsRateLimiter,
asyncMiddleware(generateWatchHtmlPage)
)
clientsRouter.use([ '/accounts/:nameWithHost', '/a/:nameWithHost' ],
clientsRateLimiter,
asyncMiddleware(generateAccountHtmlPage)
)
clientsRouter.use([ '/video-channels/:nameWithHost', '/c/:nameWithHost' ],
clientsRateLimiter,
asyncMiddleware(generateVideoChannelHtmlPage)
)
clientsRouter.use('/@:nameWithHost',
clientsRateLimiter,
asyncMiddleware(generateActorHtmlPage)
)
const embedMiddlewares = [
clientsRateLimiter,
CONFIG.CSP.ENABLED
? embedCSP
: (req: express.Request, res: express.Response, next: express.NextFunction) => next(),
@ -48,11 +74,11 @@ clientsRouter.use('/video-playlists/embed', ...embedMiddlewares)
const testEmbedController = (req: express.Request, res: express.Response) => res.sendFile(testEmbedPath)
clientsRouter.use('/videos/test-embed', testEmbedController)
clientsRouter.use('/video-playlists/test-embed', testEmbedController)
clientsRouter.use('/videos/test-embed', clientsRateLimiter, testEmbedController)
clientsRouter.use('/video-playlists/test-embed', clientsRateLimiter, testEmbedController)
// Dynamic PWA manifest
clientsRouter.get('/manifest.webmanifest', asyncMiddleware(generateManifest))
clientsRouter.get('/manifest.webmanifest', clientsRateLimiter, asyncMiddleware(generateManifest))
// Static client overrides
// Must be consistent with static client overrides redirections in /support/nginx/peertube
@ -88,7 +114,10 @@ clientsRouter.use('/client/*', (req: express.Request, res: express.Response) =>
// Always serve index client page (the client is a single page application, let it handle routing)
// Try to provide the right language index.html
clientsRouter.use('/(:language)?', asyncMiddleware(serveIndexHTML))
clientsRouter.use('/(:language)?',
clientsRateLimiter,
asyncMiddleware(serveIndexHTML)
)
// ---------------------------------------------------------------------------

View File

@ -23,7 +23,7 @@ const { middleware: cacheRouteMiddleware } = cacheRouteFactory({
// ---------------------------------------------------------------------------
commentFeedsRouter.get('/feeds/video-comments.:format',
commentFeedsRouter.get('/video-comments.:format',
feedsFormatValidator,
setFeedFormatContentType,
cacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS),

View File

@ -1,13 +1,22 @@
import express from 'express'
import { CONFIG } from '@server/initializers/config'
import { buildRateLimiter } from '@server/middlewares'
import { commentFeedsRouter } from './comment-feeds'
import { videoFeedsRouter } from './video-feeds'
import { videoPodcastFeedsRouter } from './video-podcast-feeds'
const feedsRouter = express.Router()
feedsRouter.use('/', commentFeedsRouter)
feedsRouter.use('/', videoFeedsRouter)
feedsRouter.use('/', videoPodcastFeedsRouter)
const feedsRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.FEEDS.WINDOW_MS,
max: CONFIG.RATES_LIMIT.FEEDS.MAX
})
feedsRouter.use('/feeds', feedsRateLimiter)
feedsRouter.use('/feeds', commentFeedsRouter)
feedsRouter.use('/feeds', videoFeedsRouter)
feedsRouter.use('/feeds', videoPodcastFeedsRouter)
// ---------------------------------------------------------------------------

View File

@ -26,7 +26,7 @@ const { middleware: cacheRouteMiddleware } = cacheRouteFactory({
// ---------------------------------------------------------------------------
videoFeedsRouter.get('/feeds/videos.:format',
videoFeedsRouter.get('/videos.:format',
videosSortValidator,
setDefaultVideosSort,
feedsFormatValidator,
@ -37,7 +37,7 @@ videoFeedsRouter.get('/feeds/videos.:format',
asyncMiddleware(generateVideoFeed)
)
videoFeedsRouter.get('/feeds/subscriptions.:format',
videoFeedsRouter.get('/subscriptions.:format',
videosSortValidator,
setDefaultVideosSort,
feedsFormatValidator,

View File

@ -40,7 +40,7 @@ for (const event of ([ 'channel-updated', 'channel-deleted' ] as const)) {
// ---------------------------------------------------------------------------
videoPodcastFeedsRouter.get('/feeds/podcast/videos.xml',
videoPodcastFeedsRouter.get('/podcast/videos.xml',
setFeedPodcastContentType,
videoFeedsPodcastSetCacheKey,
podcastCacheRouteMiddleware(ROUTE_CACHE_LIFETIME.FEEDS),

View File

@ -1,6 +1,6 @@
export * from './activitypub'
export * from './api'
export * from './bots'
export * from './sitemap'
export * from './client'
export * from './download'
export * from './feeds'

View File

@ -7,7 +7,7 @@ import { HttpStatusCode } from '@shared/models'
import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION, ROUTE_CACHE_LIFETIME } from '../initializers/constants'
import { getThemeOrDefault } from '../lib/plugins/theme-utils'
import { asyncMiddleware } from '../middlewares'
import { apiRateLimiter, asyncMiddleware } from '../middlewares'
import { cacheRoute } from '../middlewares/cache/cache'
import { UserModel } from '../models/user/user'
import { VideoModel } from '../models/video/video'
@ -18,12 +18,14 @@ const miscRouter = express.Router()
miscRouter.use(cors())
miscRouter.use('/nodeinfo/:version.json',
apiRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
asyncMiddleware(generateNodeinfo)
)
// robots.txt service
miscRouter.get('/robots.txt',
apiRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
(_, res: express.Response) => {
res.type('text/plain')
@ -33,12 +35,14 @@ miscRouter.get('/robots.txt',
)
miscRouter.all('/teapot',
apiRateLimiter,
getCup,
asyncMiddleware(serveIndexHTML)
)
// security.txt service
miscRouter.get('/security.txt',
apiRateLimiter,
(_, res: express.Response) => {
return res.redirect(HttpStatusCode.MOVED_PERMANENTLY_301, '/.well-known/security.txt')
}

View File

@ -1,6 +1,8 @@
import express from 'express'
import { join } from 'path'
import { logger } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { buildRateLimiter } from '@server/middlewares'
import { optionalAuthenticate } from '@server/middlewares/auth'
import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n'
import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
@ -18,57 +20,72 @@ const sendFileOptions = {
const pluginsRouter = express.Router()
const pluginsRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.PLUGINS.WINDOW_MS,
max: CONFIG.RATES_LIMIT.PLUGINS.MAX
})
pluginsRouter.get('/plugins/global.css',
pluginsRateLimiter,
servePluginGlobalCSS
)
pluginsRouter.get('/plugins/translations/:locale.json',
pluginsRateLimiter,
getPluginTranslations
)
pluginsRouter.get('/plugins/:pluginName/:pluginVersion/auth/:authName',
pluginsRateLimiter,
getPluginValidator(PluginType.PLUGIN),
getExternalAuthValidator,
handleAuthInPlugin
)
pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
pluginsRateLimiter,
getPluginValidator(PluginType.PLUGIN),
pluginStaticDirectoryValidator,
servePluginStaticDirectory
)
pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
pluginsRateLimiter,
getPluginValidator(PluginType.PLUGIN),
pluginStaticDirectoryValidator,
servePluginClientScripts
)
pluginsRouter.use('/plugins/:pluginName/router',
pluginsRateLimiter,
getPluginValidator(PluginType.PLUGIN, false),
optionalAuthenticate,
servePluginCustomRoutes
)
pluginsRouter.use('/plugins/:pluginName/:pluginVersion/router',
pluginsRateLimiter,
getPluginValidator(PluginType.PLUGIN),
optionalAuthenticate,
servePluginCustomRoutes
)
pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
pluginsRateLimiter,
getPluginValidator(PluginType.THEME),
pluginStaticDirectoryValidator,
servePluginStaticDirectory
)
pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
pluginsRateLimiter,
getPluginValidator(PluginType.THEME),
pluginStaticDirectoryValidator,
servePluginClientScripts
)
pluginsRouter.get('/themes/:themeName/:themeVersion/css/:staticEndpoint(*)',
pluginsRateLimiter,
serveThemeCSSValidator,
serveThemeCSSDirectory
)

View File

@ -2,17 +2,19 @@ import express from 'express'
import { MChannelSummary } from '@server/types/models'
import { escapeHTML } from '@shared/core-utils/renderer'
import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants'
import { asyncMiddleware, oembedValidator } from '../middlewares'
import { apiRateLimiter, asyncMiddleware, oembedValidator } from '../middlewares'
import { accountNameWithHostGetValidator } from '../middlewares/validators'
import { forceNumber } from '@shared/core-utils'
const servicesRouter = express.Router()
servicesRouter.use('/oembed',
apiRateLimiter,
asyncMiddleware(oembedValidator),
generateOEmbed
)
servicesRouter.use('/redirect/accounts/:accountName',
apiRateLimiter,
asyncMiddleware(accountNameWithHostGetValidator),
redirectToAccountUrl
)

View File

@ -5,17 +5,16 @@ import { logger } from '@server/helpers/logger'
import { getServerActor } from '@server/models/application/application'
import { buildNSFWFilter } from '../helpers/express-utils'
import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
import { asyncMiddleware } from '../middlewares'
import { apiRateLimiter, asyncMiddleware } from '../middlewares'
import { cacheRoute } from '../middlewares/cache/cache'
import { AccountModel } from '../models/account/account'
import { VideoModel } from '../models/video/video'
import { VideoChannelModel } from '../models/video/video-channel'
const botsRouter = express.Router()
const sitemapRouter = express.Router()
// Special route that add OpenGraph and oEmbed tags
// Do not use a template engine for a so little thing
botsRouter.use('/sitemap.xml',
sitemapRouter.use('/sitemap.xml',
apiRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP),
asyncMiddleware(getSitemap)
)
@ -23,7 +22,7 @@ botsRouter.use('/sitemap.xml',
// ---------------------------------------------------------------------------
export {
botsRouter
sitemapRouter
}
// ---------------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
import cors from 'cors'
import express from 'express'
import { join } from 'path'
import { asyncMiddleware, handleStaticError, webfingerValidator } from '@server/middlewares'
import { asyncMiddleware, buildRateLimiter, handleStaticError, webfingerValidator } from '@server/middlewares'
import { root } from '@shared/core-utils'
import { CONFIG } from '../initializers/config'
import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
@ -9,14 +9,21 @@ import { cacheRoute } from '../middlewares/cache/cache'
const wellKnownRouter = express.Router()
const wellKnownRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.WELL_KNOWN.WINDOW_MS,
max: CONFIG.RATES_LIMIT.WELL_KNOWN.MAX
})
wellKnownRouter.use(cors())
wellKnownRouter.get('/.well-known/webfinger',
wellKnownRateLimiter,
asyncMiddleware(webfingerValidator),
webfingerController
)
wellKnownRouter.get('/.well-known/security.txt',
wellKnownRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.SECURITYTXT),
(_, res: express.Response) => {
res.type('text/plain')
@ -26,6 +33,7 @@ wellKnownRouter.get('/.well-known/security.txt',
// nodeinfo service
wellKnownRouter.use('/.well-known/nodeinfo',
wellKnownRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
(_, res: express.Response) => {
return res.json({
@ -41,6 +49,7 @@ wellKnownRouter.use('/.well-known/nodeinfo',
// dnt-policy.txt service (see https://www.eff.org/dnt-policy)
wellKnownRouter.use('/.well-known/dnt-policy.txt',
wellKnownRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.DNT_POLICY),
(_, res: express.Response) => {
res.type('text/plain')
@ -51,18 +60,21 @@ wellKnownRouter.use('/.well-known/dnt-policy.txt',
// dnt service (see https://www.w3.org/TR/tracking-dnt/#status-resource)
wellKnownRouter.use('/.well-known/dnt/',
wellKnownRateLimiter,
(_, res: express.Response) => {
res.json({ tracking: 'N' })
}
)
wellKnownRouter.use('/.well-known/change-password',
wellKnownRateLimiter,
(_, res: express.Response) => {
res.redirect('/my-account/settings')
}
)
wellKnownRouter.use('/.well-known/host-meta',
wellKnownRateLimiter,
(_, res: express.Response) => {
res.type('application/xml')
@ -76,6 +88,7 @@ wellKnownRouter.use('/.well-known/host-meta',
)
wellKnownRouter.use('/.well-known/',
wellKnownRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.WELL_KNOWN),
express.static(CONFIG.STORAGE.WELL_KNOWN_DIR, { fallthrough: false }),
handleStaticError

View File

@ -56,7 +56,11 @@ function checkMissedConfig () {
'followers.instance.enabled', 'followers.instance.manual_approval',
'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces',
'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration',
'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
'rates_limit.api.window', 'rates_limit.api.max', 'rates_limit.login.window', 'rates_limit.login.max',
'rates_limit.signup.window', 'rates_limit.signup.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
'rates_limit.receive_client_log.window', 'rates_limit.receive_client_log.max', 'rates_limit.plugins.window', 'rates_limit.plugins.max',
'rates_limit.well_known.window', 'rates_limit.well_known.max', 'rates_limit.feeds.window', 'rates_limit.feeds.max',
'rates_limit.activity_pub.window', 'rates_limit.activity_pub.max', 'rates_limit.client.window', 'rates_limit.client.max',
'static_files.private_files_require_auth',
'object_storage.enabled', 'object_storage.endpoint', 'object_storage.region', 'object_storage.upload_acl.public',
'object_storage.upload_acl.private', 'object_storage.proxy.proxify_private_files', 'object_storage.credentials.access_key_id',

View File

@ -183,6 +183,26 @@ const CONFIG = {
ASK_SEND_EMAIL: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')),
MAX: config.get<number>('rates_limit.ask_send_email.max')
},
PLUGINS: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.plugins.window')),
MAX: config.get<number>('rates_limit.plugins.max')
},
WELL_KNOWN: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.well_known.window')),
MAX: config.get<number>('rates_limit.well_known.max')
},
FEEDS: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.feeds.window')),
MAX: config.get<number>('rates_limit.feeds.max')
},
ACTIVITY_PUB: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.activity_pub.window')),
MAX: config.get<number>('rates_limit.activity_pub.max')
},
CLIENT: {
WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.client.window')),
MAX: config.get<number>('rates_limit.client.max')
}
},
TRUST_PROXY: config.get<string[]>('trust_proxy'),

View File

@ -45,6 +45,11 @@ export const apiRateLimiter = buildRateLimiter({
max: CONFIG.RATES_LIMIT.API.MAX
})
export const activityPubRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.ACTIVITY_PUB.WINDOW_MS,
max: CONFIG.RATES_LIMIT.ACTIVITY_PUB.MAX
})
// ---------------------------------------------------------------------------
// Private
// ---------------------------------------------------------------------------

View File

@ -114,7 +114,7 @@ describe('Test video sources API validator', function () {
await server.videos.replaceSourceFile({
fixture: 'video_short_fake.webm',
videoId,
expectedStatus: HttpStatusCode.UNPROCESSABLE_ENTITY_422
completedExpectedStatus: HttpStatusCode.UNPROCESSABLE_ENTITY_422
})
await server.videos.replaceSourceFile({

View File

@ -23,7 +23,7 @@ describe('Test ActivityPub playlists search', function () {
let command: SearchCommand
before(async function () {
this.timeout(120000)
this.timeout(240000)
servers = await createMultipleServers(2)