diff --git a/.changeset/neat-toes-report.md b/.changeset/neat-toes-report.md new file mode 100644 index 000000000..569fa23b2 --- /dev/null +++ b/.changeset/neat-toes-report.md @@ -0,0 +1,51 @@ +--- +'@verdaccio/api': minor +'@verdaccio/auth': minor +'@verdaccio/cli': minor +'@verdaccio/config': minor +'@verdaccio/commons-api': minor +'@verdaccio/file-locking': minor +'verdaccio-htpasswd': minor +'@verdaccio/local-storage': minor +'@verdaccio/readme': minor +'@verdaccio/streams': minor +'@verdaccio/types': minor +'@verdaccio/hooks': minor +'@verdaccio/loaders': minor +'@verdaccio/logger': minor +'@verdaccio/logger-prettify': minor +'@verdaccio/middleware': minor +'@verdaccio/mock': minor +'@verdaccio/node-api': minor +'@verdaccio/active-directory': minor +'verdaccio-audit': minor +'verdaccio-auth-memory': minor +'verdaccio-aws-s3-storage': minor +'verdaccio-google-cloud': minor +'verdaccio-memory': minor +'@verdaccio/ui-theme': minor +'@verdaccio/proxy': minor +'@verdaccio/server': minor +'@verdaccio/store': minor +'@verdaccio/dev-types': minor +'@verdaccio/utils': minor +'verdaccio': minor +'@verdaccio/web': minor +--- + +feat: add server rate limit protection to all request + +To modify custom values, use the server settings property. + +```markdown +server: + +## https://www.npmjs.com/package/express-rate-limit#configuration-options + +rateLimit: +windowMs: 1000 +max: 10000 +``` + +The values are intended to be high, if you want to improve security of your server consider +using different values. diff --git a/packages/config/src/conf/default.yaml b/packages/config/src/conf/default.yaml index 9dced2110..d30415ccb 100644 --- a/packages/config/src/conf/default.yaml +++ b/packages/config/src/conf/default.yaml @@ -63,11 +63,12 @@ packages: # if package is not available locally, proxy requests to 'npmjs' registry proxy: npmjs -# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections. -# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout. -# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough. server: + # deprecated keepAliveTimeout: 60 +# rateLimit: +# windowMs: 1000 +# max: 10000 middlewares: audit: diff --git a/packages/config/src/config.ts b/packages/config/src/config.ts index 91d74e008..8fd6ec0fc 100644 --- a/packages/config/src/config.ts +++ b/packages/config/src/config.ts @@ -10,6 +10,7 @@ import { ConfigRuntime, Security, PackageAccess, + ServerSettingsConf, AuthConf, } from '@verdaccio/types'; @@ -18,6 +19,7 @@ import { getMatchedPackagesSpec, normalisePackageAccess } from './package-access import { sanityCheckUplinksProps, uplinkSanityCheck } from './uplinks'; import { defaultSecurity } from './security'; import { getUserAgent } from './agent'; +import serverSettings from './serverSettings'; const strategicConfigProps = ['uplinks', 'packages']; const allowedEnvConfig = ['http_proxy', 'https_proxy', 'no_proxy']; @@ -42,6 +44,7 @@ class Config implements AppConfig { public storage: string | void; public plugins: string | void; public security: Security; + public serverSettings: ServerSettingsConf; // @ts-ignore public secret: string; @@ -51,6 +54,7 @@ class Config implements AppConfig { this.config_path = config.config_path; this.plugins = config.plugins; this.security = _.merge(defaultSecurity, config.security); + this.serverSettings = serverSettings; for (const configProp in config) { if (self[configProp] == null) { diff --git a/packages/config/src/serverSettings.ts b/packages/config/src/serverSettings.ts new file mode 100644 index 000000000..608c94b2d --- /dev/null +++ b/packages/config/src/serverSettings.ts @@ -0,0 +1,10 @@ +export default { + // https://www.npmjs.com/package/express-rate-limit + // values are intended to be high, please customize based on own needs + rateLimit: { + windowMs: 1000, + max: 10000, + }, + // deprecated + keepAliveTimeout: 60, +}; diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts index 1238b0de3..a133ea014 100644 --- a/packages/core/types/index.d.ts +++ b/packages/core/types/index.d.ts @@ -336,6 +336,17 @@ declare module '@verdaccio/types' { token?: boolean; search?: boolean; } + export type RateLimit = { + windowMs: number; + max: number; + }; + + export type ServerSettingsConf = { + // express-rate-limit settings + rateLimit: RateLimit; + // deprecated + keepAliveTimeout?: number; + }; interface ConfigYaml { _debug?: boolean; @@ -360,6 +371,7 @@ declare module '@verdaccio/types' { middlewares?: any; filters?: any; url_prefix?: string; + server?: ServerSettingsConf; flags?: ConfigFlags; } diff --git a/packages/server/package.json b/packages/server/package.json index bd1633ac3..e8e60d1c9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -27,6 +27,7 @@ "@verdaccio/web": "workspace:5.0.0-alpha.2", "compression": "1.7.4", "cors": "2.8.5", + "express-rate-limit": "5.2.3", "express": "4.17.1", "lodash": "4.17.15" }, diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 1f0a99b06..3f15c66d2 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import express, { Application } from 'express'; import compression from 'compression'; import cors from 'cors'; +import RateLimit from 'express-rate-limit'; import { HttpError } from 'http-errors'; import { Storage } from '@verdaccio/store'; @@ -36,10 +37,12 @@ interface IPluginMiddleware extends IPlugin { const defineAPI = function (config: IConfig, storage: IStorageHandler): any { const auth: IAuth = new Auth(config); const app: Application = express(); + const limiter = new RateLimit(config.serverSettings.rateLimit); // run in production mode by default, just in case // it shouldn't make any difference anyway app.set('env', process.env.NODE_ENV || 'production'); app.use(cors()); + app.use(limiter); // Router setup app.use(log(config)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a566f2077..e16bc7d3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -828,6 +828,7 @@ importers: compression: 1.7.4 cors: 2.8.5 express: 4.17.1 + express-rate-limit: 5.2.3 lodash: 4.17.15 devDependencies: '@verdaccio/mock': 'link:../mock' @@ -850,6 +851,7 @@ importers: compression: 1.7.4 cors: 2.8.5 express: 4.17.1 + express-rate-limit: 5.2.3 http-errors: 1.7.3 lodash: 4.17.15 request: 2.87.0 @@ -12765,6 +12767,10 @@ packages: graphql: ^14.4.1 resolution: integrity: sha512-wccd9Lb6oeJ8yHpUs/8LcnGjFUUQYmOG9A5BNLybRdCzGw0PeUrtBxsIR8bfiur6uSW4OvPkVDoYH06z6/N9+w== + /express-rate-limit/5.2.3: + dev: false + resolution: + integrity: sha512-cjQH+oDrEPXxc569XvxhHC6QXqJiuBT6BhZ70X3bdAImcnHnTNMVuMAJaT0TXPoRiEErUrVPRcOTpZpM36VbOQ== /express/4.17.1: dependencies: accepts: 1.3.7