Rate Limiting¶
Added in version 3.3.0
Nautobot has the ability to rate limit requests received in order to mitigate over usage.
Rate limiting is applied for each user's provisioned token.
Rate limiting is enforced using a complexity cost model.
Nautobot estimates how expensive each request is and charges that estimate for each token's budget.
Rate limiting is disabled by default.
Requirements¶
Rate limiting budgets are stored in Redis. See Redis for setup instructions.
If Redis cannot be reached, the rate limiting fails open. Meaning, the request is served and the response reports the full budget. The failure is counted in the nautobot_rate_limiting_rest_backend_errors Prometheus metric. See Prometheus Metrics.
Budget and Window¶
The per-token budget is specified using the NAUTOBOT_RATE_LIMITING_REST_BUDGET setting. [default 1000].
The per-token budget is renewed after its window expires.
The per-token budget window is specified using the NAUTOBOT_RATE_LIMITING_REST_BUDGET_WINDOW_IN_SECONDS setting. [default 60].
The per-token budget window only begins on the first request that is charged.
The per-token budget resets when the budget window expires. Charges made during a per-token budget window do not extend the budget window.
REST Rate Limiting¶
REST Rate Limiting Modes¶
Rate limiting can be enabled using the NAUTOBOT_RATE_LIMITING_REST_MODE setting.
| Mode | Behavior |
|---|---|
off |
[DEFAULT] Requests are not charged or limited. Budget information is not included in response headers |
report |
Each request's cost is calculated and reported in the response headers. No budget is consumed and no request is denied. |
enforce |
Each request's cost is charged against the token's budget. Requests are denied once the budget is exhausted. |
Use report first to see what your clients actually cost, then switch to enforce.
Note
In report mode the RateLimit header always reports a full budget. It shows you the per-request cost, but not which requests enforce would have rejected.
REST Complexity Cost Calculation¶
Each request has a level of complexity associated with it.
Complexity is an estimate of how much work the Nautobot instance will need to perform to satisfy the request.
Complexity costs are determined in one of two forms, read operations and write operations.
Read operations are GET, HEAD, and OPTIONS requests.
Write operations are DELETE, PATCH, POST, PUT requests.
Complexity cost is generated by determining the Nautobot features used for a REST request. This is done using the classify_rest_read_request_features function which returns a RestReadRequestFeatures object. The RestReadRequestFeatures object is used by estimate_rest_read_request_cost to return an integer value representing the request's estimated cost.
The complexity cost process can be overridden using a custom function. See Custom Complexity Cost Function
REST Complexity Cost Settings¶
See Settings for the full reference on each setting.
REST Response Headers¶
When a rate limiting mode is set to report or enforce, Nautobot adds headers to REST API responses.
Rate limiting response headers follow the IETF HTTP API RateLimit Headers draft specification.
| Header | Description |
|---|---|
X-Nautobot-Cost |
The estimated cost of this request. |
RateLimit-Policy |
The budget specification: q is the budget, w is the budget window length. |
RateLimit |
The current budget state: r is the budget remaining, t is the seconds remaining in the budget window. |
Example:
RateLimit-Policy: "rest-complexity-cost";q=1000;w=60
RateLimit: "rest-complexity-cost";r=996;t=60
X-Nautobot-Cost: 4
GraphQL Rate Limiting¶
GraphQL Rate Limiting Modes¶
Rate limiting can be enabled using the NAUTOBOT_RATE_LIMITING_GRAPHQL_MODE setting.
| Mode | Behavior |
|---|---|
off |
[DEFAULT] Requests are not charged or limited. Budget information is not included in response headers |
report |
Each request's cost is calculated and reported in the response headers. No budget is consumed and no request is denied. |
enforce |
Each request's cost is charged against the token's budget. Requests are denied once the budget is exhausted. |
Use report first to see what your clients actually cost, then switch to enforce.
GraphQL Complexity Cost Calculation¶
Warning
GraphQL complexity cost rate limiting has not yet been implemented
GraphQL Complexity Cost Settings¶
Warning
GraphQL complexity cost rate limiting has not yet been implemented
| Setting | Default | Description |
|---|---|---|
NAUTOBOT_RATE_LIMITING_GRAPHQL_MODE |
off |
Mode for token-authenticated GraphQL requests. |
See Settings for the full reference on each setting.
Denied Requests¶
When rate limiting is enforced, a request made after the budget is exhausted returns an HTTP 429 Too Many Requests status.
The response headers will carry a Retry-After header with the number of seconds remaining in the window.
Custom Complexity Cost Function¶
NAUTOBOT_RATE_LIMITING_CUSTOM_COMPLEXITY_COST_ESTIMATION_FUNCTION replaces Nautobot's built-in complexity cost estimation with a custom implementation.
Nautobot resolves the path at startup and reports a system check error if it cannot be imported or is not callable.
When set to an empty string (the default) Nautobot uses the built-in estimation implementation.
Set it to a dotted import path to override.
The dotted path must be importable, so the function has to live in an installed Python package, in a Nautobot App, or in nautobot_config.py itself (referenced as nautobot_config.my_function). Nautobot does not add any directory to sys.path, so a loose file beside nautobot_config.py is not importable on its own.
For example my_package.my_module.my_function. Or nautobot_config.estimate_request_cost.
The custom function takes one argument, the HttpRequest being estimated. The custom function returns an integer, the cost of the request. The returned integer is used to charge the token for its request.
This function is applied to REST read and write operations, as well as GraphQL operations.
Custom Complexity Cost Function - Flat Example¶
# nautobot_config.py
NAUTOBOT_RATE_LIMITING_REST_MODE = "enforce"
NAUTOBOT_RATE_LIMITING_CUSTOM_COMPLEXITY_COST_ESTIMATION_FUNCTION = "nautobot_config.estimate_request_cost"
def estimate_request_cost(request):
"""Charge a flat cost for every API request."""
return 1
Custom Complexity Cost Function - Using RestReadRequestFeatures Object¶
# nautobot_config.py
from nautobot.core.rate_limiting.rest_calculator import (
READ_METHODS,
classify_rest_read_request_features,
)
NAUTOBOT_RATE_LIMITING_REST_MODE = "enforce"
NAUTOBOT_RATE_LIMITING_CUSTOM_COMPLEXITY_COST_ESTIMATION_FUNCTION = "nautobot_config.estimate_request_cost"
def estimate_request_cost(request):
if request.method not in READ_METHODS:
return 10
rest_read_request_features = classify_rest_read_request_features(request)
cost = 1
cost += rest_read_request_features.depth * 2
cost += rest_read_request_features.indexable_lookups
cost += rest_read_request_features.unindexable_lookups * 5
if rest_read_request_features.do_computed_fields_exist():
cost += 10
if rest_read_request_features.is_response_format_csv():
cost *= 3
return cost
Prometheus Monitoring¶
When rate limiting is enabled, Prometheus metrics will be generated. See Prometheus Metrics.