feat: add rate limit allow list

This commit is contained in:
Rainer Killinger
2022-03-03 12:06:51 +01:00
parent c03b5d3faa
commit f10cd6c431
6 changed files with 413 additions and 356 deletions

View File

@@ -16,10 +16,11 @@
import {ConfigFile} from '../src/common'; import {ConfigFile} from '../src/common';
const config: ConfigFile = { const config: ConfigFile = {
activeVersions: ['1\\.0\\.\\d+'], activeVersions: ['1\\.0\\.\\d+','2\\.0\\.\\d+'],
hiddenRoutes: ['/bulk'], hiddenRoutes: ['/bulk'],
outdatedVersions: ['0\\.8\\.\\d+', '0\\.5\\.\\d+', '0\\.6\\.\\d+', '0\\.7\\.\\d+'], outdatedVersions: ['0\\.8\\.\\d+', '0\\.5\\.\\d+', '0\\.6\\.\\d+', '0\\.7\\.\\d+'],
output: '/etc/nginx/conf.d/default.conf', output: '/etc/nginx/conf.d/default.conf',
rateLimitAllowList: ['127.0.0.1/32'],
sslFilePaths: { sslFilePaths: {
certificate: '/etc/nginx/certs/ssl.crt', certificate: '/etc/nginx/certs/ssl.crt',
certificateChain: '/etc/nginx/certs/chain.crt', certificateChain: '/etc/nginx/certs/chain.crt',

View File

@@ -3,7 +3,18 @@
# create a custom request limit zone which can handle 160,000 IP-Addresses at the same time # create a custom request limit zone which can handle 160,000 IP-Addresses at the same time
# routes using this limit zone will limit each client to not send more than one request in 50ms # routes using this limit zone will limit each client to not send more than one request in 50ms
# be sure to use burst handling when needed, because most clients will fire some requests in parallel # be sure to use burst handling when needed, because most clients will fire some requests in parallel
limit_req_zone $binary_remote_addr zone=customstappslimit:10m rate=20r/s;
geo $isRateLimited {
default 1;
{{{ rateLimitAllowList }}}
}
map $isRateLimited $rateLimit {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $rateLimit zone=customstappslimit:10m rate=20r/s;
server { server {
{{{ listener }}} {{{ listener }}}

731
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
"@types/sha1": "1.1.3", "@types/sha1": "1.1.3",
"config": "3.3.6", "config": "3.3.6",
"dockerode": "3.3.0", "dockerode": "3.3.0",
"is-cidr": "4.0.2",
"mustache": "4.2.0", "mustache": "4.2.0",
"semver": "7.3.5" "semver": "7.3.5"
}, },

View File

@@ -67,6 +67,10 @@ export interface ConfigFile {
* Output?! TODO * Output?! TODO
*/ */
output: string; output: string;
/**
* Allow list for rate limiting
*/
rateLimitAllowList: string[];
/** /**
* SSL file paths * SSL file paths
*/ */
@@ -89,6 +93,10 @@ export interface TemplateView {
* Listener * Listener
*/ */
listener: string; listener: string;
/**
* Allow list for rate limiting
*/
rateLimitAllowList: string;
/** /**
* Static route * Static route
*/ */

View File

@@ -15,6 +15,7 @@
*/ */
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import Dockerode from 'dockerode'; import Dockerode from 'dockerode';
import isCidr from 'is-cidr';
import {render} from 'mustache'; import {render} from 'mustache';
import {join} from 'path'; import {join} from 'path';
import * as semver from 'semver'; import * as semver from 'semver';
@@ -187,6 +188,17 @@ async function renderTemplate(path: string, view: unknown): Promise<string> {
return render(content, view); return render(content, view);
} }
/**
* Generate allow list entries in CIDR notation that pass thru rate limiting
*
* @param entries Allow list entries that should be in CIDR notation
*/
function generateRateLimitAllowList(entries: string[]): string {
return entries.filter(entry => isCidr(entry))
.map(entry => `${entry} 0;`)
.join('\n');
}
/** /**
* Returns view for nginx config file * Returns view for nginx config file
* *
@@ -213,6 +225,7 @@ export async function getTemplateView(containers: Dockerode.ContainerInfo[]): Pr
dockerVersionMap: await generateUpstreamMap(configFile.activeVersions, configFile.outdatedVersions, containers), dockerVersionMap: await generateUpstreamMap(configFile.activeVersions, configFile.outdatedVersions, containers),
hiddenRoutes: (await Promise.all(hiddenRoutesPromises)).join(''), hiddenRoutes: (await Promise.all(hiddenRoutesPromises)).join(''),
listener: generateListener(configFile.sslFilePaths), listener: generateListener(configFile.sslFilePaths),
rateLimitAllowList: generateRateLimitAllowList(configFile.rateLimitAllowList),
staticRoute: await renderTemplate(join('fixtures', 'staticRoute.template'), {cors}), staticRoute: await renderTemplate(join('fixtures', 'staticRoute.template'), {cors}),
visibleRoutes: (await Promise.all(visibleRoutesPromises)).join(''), visibleRoutes: (await Promise.all(visibleRoutesPromises)).join(''),
}; };