Class TransportRateLimitPartitions
- Namespace
- MailFathom.Infrastructure.Security.Transport
- Assembly
- MailFathom.Infrastructure.dll
Whose capacity a request spends, on which transport surface.
public static class TransportRateLimitPartitions
- Inheritance
-
TransportRateLimitPartitions
- Inherited Members
Remarks
A per-caller limit needs a name to keep the count under, and which name it is decides whether the limit protects the
surface or becomes the way to bring it down. Every partition a request can name is a dictionary entry the process
keeps for as long as the limiter does, so a key an attacker chooses is memory an attacker allocates. This type
therefore admits exactly one source of names: the identity authentication established. Nothing a caller writes into
a header, a path, a query string, an Origin, or a user agent reaches a key, and no forwarded address does
either, because a proxy header is chosen by whoever is upstream and trusting one is a separate design with its own
review.
The surface leads every key, which is what keeps two endpoints' capacity independent no matter what they are configured with. A key list an operator wrote for the administrative endpoint may spell the same names as the MCP endpoint's — the two sections are configured separately and neither consults the other — and without the prefix a client named identically on both would spend one bucket from both surfaces. It also holds for the anonymous partition, which is the one every surface has and the one a flood of bad credentials lands in.
Everything else shares one anonymous partition per surface. That is deliberately coarse: under
None there is no identity to tell one caller from another, so a single bucket is the only bound that cannot be
grown by asking for it, and unauthenticated callers being able to exhaust each other's capacity is a smaller problem
than unauthenticated callers being able to exhaust the process's memory. The same partition absorbs a request whose
credential was refused, so a flood of bad credentials is counted and limited rather than served for free.
Two identities can be established at once, and the rule between them is fixed rather than merged. A configured API key names one client of this deployment, which is what the operator partitioned their clients into; a client certificate profile names a client *application*, and several keys may sit behind one profile. Taking the key wherever there is one therefore keeps the partitions exactly as narrow as the key list, while taking the profile would let one key starve another that happens to share its certificate. Combining the two is the rule not taken: a pair-shaped key would hand the same credential a second bucket for every profile it could present under, which is capacity bought by holding one more certificate.
The remote address is deliberately not part of any key, not even as a fallback. It is spoofable on the traffic this is aimed at, and one client behind a shared address would otherwise be limited by another's behaviour.
Fields
- AnonymousIdentity
The one identity every request on a surface without an authenticated identity is counted under.
Methods
- KeyFor(string, string?, string?)
Names the partition a request's capacity is taken from.