Merge pull request #1561 from cdr/ratelimit
Add basic rate limiting to login endpoint
This commit is contained in:
commit
05456024c4
|
@ -21,3 +21,4 @@ extends:
|
||||||
rules:
|
rules:
|
||||||
# For overloads.
|
# For overloads.
|
||||||
no-dupe-class-members: off
|
no-dupe-class-members: off
|
||||||
|
"@typescript-eslint/no-use-before-define": off
|
||||||
|
|
|
@ -52,6 +52,8 @@ randomly generated password so you can use that. You can set the `PASSWORD` envi
|
||||||
to use your own instead. If you want to handle authentication yourself, use `--auth none`
|
to use your own instead. If you want to handle authentication yourself, use `--auth none`
|
||||||
to disable password authentication.
|
to disable password authentication.
|
||||||
|
|
||||||
|
**note**: code-server will rate limit password authentication attempts at 2 a minute and 12 an hour.
|
||||||
|
|
||||||
If you want to use external authentication you should handle this with a reverse
|
If you want to use external authentication you should handle this with a reverse
|
||||||
proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy).
|
proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy).
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"http-proxy": "^1.18.0",
|
"http-proxy": "^1.18.0",
|
||||||
"httpolyglot": "^0.1.2",
|
"httpolyglot": "^0.1.2",
|
||||||
|
"limiter": "^1.1.5",
|
||||||
"node-pty": "^0.9.0",
|
"node-pty": "^0.9.0",
|
||||||
"pem": "^1.14.2",
|
"pem": "^1.14.2",
|
||||||
"safe-compare": "^1.1.4",
|
"safe-compare": "^1.1.4",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
|
import * as limiter from "limiter"
|
||||||
import * as querystring from "querystring"
|
import * as querystring from "querystring"
|
||||||
import { HttpCode, HttpError } from "../../common/http"
|
import { HttpCode, HttpError } from "../../common/http"
|
||||||
import { AuthType, HttpProvider, HttpResponse, Route } from "../http"
|
import { AuthType, HttpProvider, HttpResponse, Route } from "../http"
|
||||||
|
@ -48,6 +49,8 @@ export class LoginHttpProvider extends HttpProvider {
|
||||||
return this.replaceTemplates(route, response)
|
return this.replaceTemplates(route, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly limiter = new RateLimiter()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try logging in. On failure, show the login page with an error.
|
* Try logging in. On failure, show the login page with an error.
|
||||||
*/
|
*/
|
||||||
|
@ -59,6 +62,10 @@ export class LoginHttpProvider extends HttpProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!this.limiter.try()) {
|
||||||
|
throw new Error("Login rate limited!")
|
||||||
|
}
|
||||||
|
|
||||||
const data = await this.getData(request)
|
const data = await this.getData(request)
|
||||||
const payload = data ? querystring.parse(data) : {}
|
const payload = data ? querystring.parse(data) : {}
|
||||||
return await this.login(payload, route, request)
|
return await this.login(payload, route, request)
|
||||||
|
@ -108,3 +115,17 @@ export class LoginHttpProvider extends HttpProvider {
|
||||||
throw new Error("Missing password")
|
throw new Error("Missing password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateLimiter wraps around the limiter library for logins.
|
||||||
|
// It allows 2 logins every minute and 12 logins every hour.
|
||||||
|
class RateLimiter {
|
||||||
|
private readonly minuteLimiter = new limiter.RateLimiter(2, "minute")
|
||||||
|
private readonly hourLimiter = new limiter.RateLimiter(12, "hour")
|
||||||
|
|
||||||
|
public try(): boolean {
|
||||||
|
if (this.minuteLimiter.tryRemoveTokens(1)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return this.hourLimiter.tryRemoveTokens(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4044,6 +4044,11 @@ levn@^0.3.0, levn@~0.3.0:
|
||||||
prelude-ls "~1.1.2"
|
prelude-ls "~1.1.2"
|
||||||
type-check "~0.3.2"
|
type-check "~0.3.2"
|
||||||
|
|
||||||
|
limiter@^1.1.5:
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2"
|
||||||
|
integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==
|
||||||
|
|
||||||
lines-and-columns@^1.1.6:
|
lines-and-columns@^1.1.6:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||||
|
|
Loading…
Reference in New Issue