Repository navigation
Optional application-layer security beyond the login (attack screening, automatic bans, per-route policies) #8060
Description
Disclosure: I maintain the Guard Core libraries used in this proposal.
What is your suggestion?
An opt-in --guard flag that screens every HTTP request and websocket upgrade before it reaches code-server's routes, using the @guardcore/express middleware (MIT, express ^4 || ^5 peer, and code-server already runs Express 5). Without the flag nothing is imported and no middleware is added.
When it's on you get:
- attack-pattern detection on the path, query string and headers (scanner traffic, path traversal, injection probes) with automatic IP bans for clients that keep sending them
- an IP and CIDR allowlist (
--guard-allow-ip, repeatable) - a trusted client IP: the address of the direct connection, with
X-Forwarded-Forhonored only from loopback by default or from--guard-trusted-proxyaddresses (repeatable), so other peers can't spoof it - per-IP rate limiting for all routes (
--guard-rate-limit, requests per minute, default 1200)
How it behaves:
- Request bodies are never scanned, so uploads, the login form and VS Code traffic aren't inspected. Path, query and headers only.
- The
ssrfdetection category is left out. Browsers send the page address asRefererand guard-core 4.4.0 flags localhost and LAN addresses in it, which would break the UI for anyone opening it on localhost or a LAN IP. I'd turn it back on when the engine fix ships. - The guard sits first on the HTTP router, so rejected requests never reach the heartbeat, static files or the login limiter. Websocket upgrades can't go through router middleware (their response isn't a real HTTP response), so the same checks run through an upgrade hook. That's what lets the allowlist, bans and rate limit cover the editor websocket and proxied ports. Allowed upgrades pass through untouched.
- State is in memory per process (no Redis), like the existing login limiter. The regular request path and the upgrade path keep separate counters.
- Security headers are left alone (
securityHeadersis disabled) because VS Code sets its own CSP. - The login limiter is unchanged and still applies on top.
What else the engine offers
I kept the branch to a small, safe subset. The rest is already in @guardcore/express and I can wire whatever you find useful:
- Per-route policies: an IP allowlist only on
/proxy/:portand/absproxy/:port, which expose ports of the development machine, and on/update, plus a strict budget with bans on/loginon top of the existing limiter. - Honeypot traps: a hidden field in the login form that no person can see. A client that fills it is a bot working through the instance, so the request is rejected.
- Behavior rules, for example banning clients that keep hitting 404s (scanners) or repeat suspicious request patterns.
- Country allow and block lists, blocking cloud-provider IP ranges, user-agent filtering.
- Emergency mode, which locks the instance down to an allowlist during an incident, and a passive (log-only) mode to try everything before enforcing it.
Why do you want this feature?
code-server is meant to be exposed to the internet, but today only /login is rate limited, with a process-wide token bucket (2 per minute plus 12 per hour) in src/node/routes/login.ts that isn't per IP. Every other route is unthrottled, there's no IP allowlist, and nothing bans scanners that probe the server. Anyone who can reach the port can hammer the static, proxy and VS Code routes.
It has come up before:
- Ratelimit code-server password auth #1320 "Ratelimit code-server password auth" (closed) asked for the login rate limit that exists today.
- Login rate limiter should not count against successful logins #2647 "Login rate limiter should not count against successful logins" (closed) refined it.
- Security Feature Request and Issues #7104 "Security Feature Request and Issues" (open) asks for customizable rate limit settings and fail2ban style integration for people who want tighter control.
This doesn't change the login limiter. It adds the missing layer for everything else, and changes nothing for anyone who doesn't opt in.
Are there any workarounds to get this functionality today?
Yes: a reverse proxy (nginx limit_req and allow/deny, Caddy, Traefik) or a network firewall for rate limits and allowlists, and fail2ban reading code-server's logs for login failures. They protect the edge and stay useful, but it's a different layer. They need infrastructure the user may not control (--trusted-origins exists for people who can't configure their proxy), fail2ban only sees the failed logins code-server logs, and none of them inspects requests for attack patterns inside the application. A built-in opt-in option complements them, behind a proxy or without one.
Are you interested in submitting a PR for this?
Yes. A tested branch is ready: https://github.com/rennf93/code-server/tree/feat/guardcore-express-security (I'll open the PR once this is discussed).
- Dependency impact: one new runtime dependency,
@guardcore/express, which brings@guardcore/core,guardagent,zodandipaddr.js(package-lock.json+100/-1, regenerated with npm 11). It's loaded with a dynamic import only when--guardis set.guardagentis the optional telemetry client of these libraries: installed, but never imported unless an agent is explicitly configured, which this change doesn't do, so nothing is sent anywhere. The release build copiesnode_modulesand the existingnpm ci/npm prune --productionflow covers it, no packaging changes. - Tests (
test/unit/node/guard.test.ts, existing integration harness): disabled adds nothing and never loads the package, 429 on a burst, 403 allowlist (HTTP and websocket), spoofed X-Forwarded-For ignored from an untrusted peer and honored from loopback, attack in the query blocked, bodies not scanned, login page and login limiter unchanged, static and VS Code style paths served, websocket upgrade unaffected. - Docs:
--helptext, a FAQ entry and a changelog entry.
Who I am: I'm rennf93. I wrote fastapi-guard, public since August 2024 (first PyPI release, 0.1.0, on 2024-08-26), now at 825 GitHub stars, 65 releases and about 62,000 PyPI downloads last month (pypistats.org). The engines and adapters for other languages and frameworks came later and run the same detection rules, checked in CI against shared conformance fixtures.
Activity
linear-code commentedon Oct 16, 2026
CODE-82
[-]Optional request screening (rate limit, IP allowlist, attack detection) beyond the login[/-][+]Optional application-layer security beyond the login (attack screening, automatic bans, per-route policies, honeypot)[/+][-]Optional application-layer security beyond the login (attack screening, automatic bans, per-route policies, honeypot)[/-][+]Optional application-layer security beyond the login (attack screening, automatic bans, per-route policies)[/+]