RC-2 0.5.2
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
# Public Edge Security (Caddy + SessionGuard EdgeGuard)
|
||||
|
||||
SessionGuard EdgeGuard is a small Go service for the public reverse-proxy host.
|
||||
It is **not** another login and it is not a WAF replacement. It runs locally on
|
||||
`127.0.0.1:9081` and is called by Caddy using `forward_auth` before traffic is
|
||||
sent to SessionGuard or a Guacamole worker.
|
||||
|
||||
## Threat model
|
||||
|
||||
EdgeGuard is intended to reduce the cost of common Internet abuse:
|
||||
|
||||
- repeated scanners and exploit probes;
|
||||
- excessive requests from one source;
|
||||
- request floods intended to exhaust SessionGuard/Guacamole rather than the
|
||||
physical Internet link;
|
||||
- malformed/oversized URIs and unexpected HTTP methods;
|
||||
- accidental exposure of backend-only SessionGuard endpoints.
|
||||
|
||||
It cannot stop a volumetric attack that saturates the VPS uplink. Provider-side
|
||||
DDoS filtering/firewalling remains necessary for that class of attack.
|
||||
|
||||
## Request flow
|
||||
|
||||
```text
|
||||
Browser
|
||||
-> Caddy TLS
|
||||
-> EdgeGuard /check (localhost only)
|
||||
-> 204: continue
|
||||
-> 403/404/405/421/429: stop at edge
|
||||
-> SessionGuard Access Auth
|
||||
-> Guacamole worker
|
||||
```
|
||||
|
||||
Caddy's `forward_auth` sends a lightweight GET subrequest. EdgeGuard therefore
|
||||
checks the initial WebSocket handshake, but it is not in the byte path of the
|
||||
established Guacamole WebSocket/RDP stream.
|
||||
|
||||
## Controls
|
||||
|
||||
### Static blacklist
|
||||
|
||||
`blacklist.txt` accepts one IPv4/IPv6 address or CIDR per line. Comments start
|
||||
with `#`. Changes are automatically picked up.
|
||||
|
||||
```text
|
||||
203.0.113.44
|
||||
198.51.100.0/24
|
||||
2001:db8:1234::/48
|
||||
```
|
||||
|
||||
### Global overload limit
|
||||
|
||||
The global token bucket is intentionally high and protects the application
|
||||
backends when a distributed HTTP flood reaches the VPS. It does not prevent
|
||||
network saturation because TLS and the incoming packets already reached Caddy.
|
||||
|
||||
Default production example:
|
||||
|
||||
```json
|
||||
"global_limit": {
|
||||
"rate_per_second": 2500,
|
||||
"burst": 5000
|
||||
}
|
||||
```
|
||||
|
||||
### Per-IP limit
|
||||
|
||||
The default is deliberately NAT-friendly:
|
||||
|
||||
```json
|
||||
"per_ip_limit": {
|
||||
"rate_per_second": 200,
|
||||
"burst": 500
|
||||
}
|
||||
```
|
||||
|
||||
For environments where many staff share one public NAT IP, do not aggressively
|
||||
lower this value. If a known source really needs exemption, put its address in
|
||||
`rate-exempt.txt`. Exempt sources still pass blacklist, scanner, method, host
|
||||
and global-overload checks.
|
||||
|
||||
### Endpoint-specific limits
|
||||
|
||||
The example uses tighter limits for OIDC login/callback paths. These endpoints
|
||||
do not contain the PocketID password check itself; the limits are intended to
|
||||
protect state/session allocation and redirect processing from floods.
|
||||
|
||||
### Bounded per-IP memory
|
||||
|
||||
`max_tracked_ips` bounds the in-memory table used for per-IP token buckets and
|
||||
offense state. The production example allows 100,000 active source addresses;
|
||||
when the table is full, previously unseen sources receive HTTP 429 instead of
|
||||
causing unbounded memory growth. Stale entries are cleaned up automatically.
|
||||
|
||||
### Scanner detection and temporary bans
|
||||
|
||||
Known irrelevant exploit/scanner paths are denied before they reach the
|
||||
backends. The defaults include `/.env`, `/.git`, WordPress, phpMyAdmin, CGI,
|
||||
Spring Actuator and several common automated exploit probes.
|
||||
|
||||
The example auto-ban weights are:
|
||||
|
||||
- scanner path: 5 points;
|
||||
- blocked method: 3 points;
|
||||
- malformed URI: 3 points;
|
||||
- ordinary rate-limit violation: 0 points.
|
||||
|
||||
At 10 points within 120 seconds, the IP is banned for 900 seconds. Bans are
|
||||
persisted in `/var/lib/sessionguard-edgeguard/state.json` with a short write
|
||||
debounce, so a container restart does not normally remove them while scanner
|
||||
floods cannot force one synchronous disk write per request.
|
||||
|
||||
Rate-limit violations deliberately have weight 0 by default to avoid banning a
|
||||
whole corporate NAT during a legitimate burst.
|
||||
|
||||
### Blocked HTTP methods
|
||||
|
||||
`CONNECT`, `TRACE` and `TRACK` are rejected. SessionGuard/Guacamole continue to
|
||||
use their normal GET/POST/PUT/PATCH/DELETE/OPTIONS behavior.
|
||||
|
||||
### Host allowlist
|
||||
|
||||
Only configured public host names are accepted by EdgeGuard. Caddy additionally
|
||||
uses `strict_sni_host on`, requiring the TLS SNI host and HTTP Host header to
|
||||
match.
|
||||
|
||||
## Caddy hardening
|
||||
|
||||
The supplied public Caddyfile also configures:
|
||||
|
||||
```caddyfile
|
||||
servers {
|
||||
protocols h1 h2
|
||||
strict_sni_host on
|
||||
max_header_size 64KB
|
||||
timeouts {
|
||||
read_header 10s
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The short header timeout and smaller header ceiling reduce slow-header/resource
|
||||
exhaustion risk. HTTP/3 is intentionally disabled in the example to reduce the
|
||||
public protocol surface; Guacamole works with HTTP/1.1/WebSocket and HTTP/2.
|
||||
|
||||
Access logs are written to size-limited rotating files. Sampling keeps all normal
|
||||
traffic but reduces log amplification once a single logger exceeds 200 entries
|
||||
per second.
|
||||
|
||||
The public SessionGuard host denies `/metrics` and `/api/v1/broker/*` at Caddy.
|
||||
The Caddy admin API is disabled (`admin off`) on this dedicated public edge;
|
||||
configuration changes are applied by restarting the Caddy container.
|
||||
Guacamole workers call broker APIs directly over NetBird instead.
|
||||
|
||||
## Metrics
|
||||
|
||||
EdgeGuard exposes Prometheus text metrics only on localhost:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:9081/metrics
|
||||
```
|
||||
|
||||
Counters include total checks, allows, static-blacklist denies, temporary-ban
|
||||
denies, rate-limit denies, scanner denies and auto-bans.
|
||||
|
||||
## Configuration reload
|
||||
|
||||
`edgeguard.json`, `blacklist.txt` and `rate-exempt.txt` are re-read on the
|
||||
configured interval (15 seconds in the example). An invalid replacement config
|
||||
is logged and the previous working configuration remains active.
|
||||
|
||||
## Operational recommendations
|
||||
|
||||
1. Keep provider/network DDoS protection enabled.
|
||||
2. Expose only TCP 80/443 publicly; keep EdgeGuard on localhost.
|
||||
3. Keep NetBird peer/backend ports private.
|
||||
4. Pin Caddy to a currently patched release rather than an old major-only image
|
||||
during controlled production rollouts.
|
||||
5. Monitor HTTP 429 and EdgeGuard auto-ban counters before tightening limits.
|
||||
6. Do not put broad office/country CIDRs on the static blacklist without first
|
||||
checking whether legitimate remote users may originate there.
|
||||
Reference in New Issue
Block a user