Skip to content

Commit 0545602

Browse files
authored
Merge pull request #1561 from cdr/ratelimit
Add basic rate limiting to login endpoint
2 parents f21ba53 + 5accf3f commit 0545602

File tree

5 files changed

+30
-0
lines changed

5 files changed

+30
-0
lines changed

.eslintrc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ extends:
2121
rules:
2222
# For overloads.
2323
no-dupe-class-members: off
24+
"@typescript-eslint/no-use-before-define": off

doc/FAQ.md

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ randomly generated password so you can use that. You can set the `PASSWORD` envi
5252
to use your own instead. If you want to handle authentication yourself, use `--auth none`
5353
to disable password authentication.
5454

55+
**note**: code-server will rate limit password authentication attempts at 2 a minute and 12 an hour.
56+
5557
If you want to use external authentication you should handle this with a reverse
5658
proxy using something like [oauth2_proxy](https://github.com/pusher/oauth2_proxy).
5759

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"fs-extra": "^8.1.0",
5454
"http-proxy": "^1.18.0",
5555
"httpolyglot": "^0.1.2",
56+
"limiter": "^1.1.5",
5657
"node-pty": "^0.9.0",
5758
"pem": "^1.14.2",
5859
"safe-compare": "^1.1.4",

src/node/app/login.ts

+21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as http from "http"
2+
import * as limiter from "limiter"
23
import * as querystring from "querystring"
34
import { HttpCode, HttpError } from "../../common/http"
45
import { AuthType, HttpProvider, HttpResponse, Route } from "../http"
@@ -48,6 +49,8 @@ export class LoginHttpProvider extends HttpProvider {
4849
return this.replaceTemplates(route, response)
4950
}
5051

52+
private readonly limiter = new RateLimiter()
53+
5154
/**
5255
* Try logging in. On failure, show the login page with an error.
5356
*/
@@ -59,6 +62,10 @@ export class LoginHttpProvider extends HttpProvider {
5962
}
6063

6164
try {
65+
if (!this.limiter.try()) {
66+
throw new Error("Login rate limited!")
67+
}
68+
6269
const data = await this.getData(request)
6370
const payload = data ? querystring.parse(data) : {}
6471
return await this.login(payload, route, request)
@@ -108,3 +115,17 @@ export class LoginHttpProvider extends HttpProvider {
108115
throw new Error("Missing password")
109116
}
110117
}
118+
119+
// RateLimiter wraps around the limiter library for logins.
120+
// It allows 2 logins every minute and 12 logins every hour.
121+
class RateLimiter {
122+
private readonly minuteLimiter = new limiter.RateLimiter(2, "minute")
123+
private readonly hourLimiter = new limiter.RateLimiter(12, "hour")
124+
125+
public try(): boolean {
126+
if (this.minuteLimiter.tryRemoveTokens(1)) {
127+
return true
128+
}
129+
return this.hourLimiter.tryRemoveTokens(1)
130+
}
131+
}

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,11 @@ levn@^0.3.0, levn@~0.3.0:
40444044
prelude-ls "~1.1.2"
40454045
type-check "~0.3.2"
40464046

4047+
limiter@^1.1.5:
4048+
version "1.1.5"
4049+
resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2"
4050+
integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==
4051+
40474052
lines-and-columns@^1.1.6:
40484053
version "1.1.6"
40494054
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"

0 commit comments

Comments
 (0)