Commit 94b019f
fix(server): harden HTTP(S) accept path against fd exhaustion (#630)
A production deployment became unreachable after five weeks of uptime:
internet scanners left ~1100 half-open TLS connections behind, each
pinning a file descriptor forever, until the process hit its soft fd
limit (1024) and accept() failed with EMFILE -- silently, because
axum-server retries accept errors without logging. The server looked
healthy (running, idle runtime) while the kernel backlog accepted TCP
connections that were never serviced.
Add layered protections in a new server_hardening module:
- KeepaliveAcceptor: enables TCP keepalive (60s + 15s probes) on
accepted connections so the kernel reclaims connections whose peer
vanished without a FIN.
- FirstByteTimeoutAcceptor: closes connections that never send a byte
within 30s of being accepted (post-TLS). hyper's header_read_timeout
cannot cover this case because hyper-util's auto builder sniffs the
h1/h2 version with an untimed read. The deadline is disarmed by the
first byte, so WebSockets and in-flight requests are unaffected.
- Explicit 10s TLS handshake timeout (matches the axum-server default,
pinned so upstream default changes cannot remove the protection).
- header_read_timeout (30s) on the hyper builder to reap idle
keep-alive connections between requests. Requires installing
TokioTimer: hyper panics per-connection if a timeout is configured
without a timer, which axum-server does not install by default.
- fd-usage watchdog (Linux): warns at 80% of the soft limit, errors
every minute at 95%, so the approach to EMFILE is visible in logs
before the server becomes unreachable.
Verified end-to-end against a running server: raw-idle closed at 10s,
TLS-idle closed at 30s, idle keep-alive closed at 30s, idle WebSocket
stays open, no hyper panics.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent f14a243 commit 94b019f
5 files changed
Lines changed: 378 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
84 | 85 | | |
85 | 86 | | |
86 | 87 | | |
| |||
142 | 143 | | |
143 | 144 | | |
144 | 145 | | |
| 146 | + | |
145 | 147 | | |
146 | 148 | | |
147 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
686 | 686 | | |
687 | 687 | | |
688 | 688 | | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
689 | 693 | | |
690 | 694 | | |
691 | 695 | | |
692 | 696 | | |
693 | 697 | | |
694 | 698 | | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
695 | 705 | | |
696 | 706 | | |
697 | | - | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
698 | 721 | | |
699 | 722 | | |
700 | 723 | | |
701 | 724 | | |
702 | 725 | | |
703 | | - | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
704 | 734 | | |
705 | 735 | | |
706 | 736 | | |
| |||
0 commit comments