mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-11 13:45:25 +03:00
use x-forwarded-for to rate limit login attempts by ip
This commit is contained in:
parent
6d6a54c5ae
commit
35f02bd505
@ -7,7 +7,7 @@ proxy_set_header X-Forwarded-Proto $scheme;
|
|||||||
proxy_set_header X-Forwarded-Host $http_host;
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
proxy_set_header X-Forwarded-URI $request_uri;
|
proxy_set_header X-Forwarded-URI $request_uri;
|
||||||
proxy_set_header X-Forwarded-Ssl on;
|
proxy_set_header X-Forwarded-Ssl on;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
## Basic Proxy Configuration
|
## Basic Proxy Configuration
|
||||||
|
|||||||
@ -87,8 +87,9 @@ def create_app(
|
|||||||
app.camera_error_image = None
|
app.camera_error_image = None
|
||||||
app.stats_emitter = stats_emitter
|
app.stats_emitter = stats_emitter
|
||||||
app.jwt_token = get_jwt_secret() if frigate_config.auth.enabled else None
|
app.jwt_token = get_jwt_secret() if frigate_config.auth.enabled else None
|
||||||
# initialize the rate limiter for the login endpoint
|
# update the request_address with the x-forwarded-for header from nginx
|
||||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
|
||||||
|
# initialize the rate limiter for the login endpoint
|
||||||
limiter.init_app(app)
|
limiter.init_app(app)
|
||||||
if frigate_config.auth.failed_login_rate_limit is None:
|
if frigate_config.auth.failed_login_rate_limit is None:
|
||||||
limiter.enabled = False
|
limiter.enabled = False
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -13,7 +14,6 @@ from pathlib import Path
|
|||||||
|
|
||||||
from flask import Blueprint, current_app, jsonify, make_response, request
|
from flask import Blueprint, current_app, jsonify, make_response, request
|
||||||
from flask_limiter import Limiter
|
from flask_limiter import Limiter
|
||||||
from flask_limiter.util import get_remote_address
|
|
||||||
from joserfc import jwt
|
from joserfc import jwt
|
||||||
from peewee import DoesNotExist
|
from peewee import DoesNotExist
|
||||||
|
|
||||||
@ -24,8 +24,51 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
AuthBp = Blueprint("auth", __name__)
|
AuthBp = Blueprint("auth", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_remote_addr():
|
||||||
|
route = list(reversed(request.headers.get("x-forwarded-for").split(",")))
|
||||||
|
logger.debug(f"IP Route: {[r for r in route]}")
|
||||||
|
trusted_proxies = []
|
||||||
|
for proxy in current_app.frigate_config.auth.trusted_proxies:
|
||||||
|
try:
|
||||||
|
network = ipaddress.ip_network(proxy)
|
||||||
|
except ValueError:
|
||||||
|
logger.warn(f"Unable to parse trusted network: {proxy}")
|
||||||
|
trusted_proxies.append(network)
|
||||||
|
|
||||||
|
# return the first remote address that is not trusted
|
||||||
|
for addr in route:
|
||||||
|
ip = ipaddress.ip_address(addr.strip())
|
||||||
|
logger.debug(f"Checking {ip} (v{ip.version})")
|
||||||
|
trusted = False
|
||||||
|
for trusted_proxy in trusted_proxies:
|
||||||
|
logger.debug(
|
||||||
|
f"Checking against trusted proxy: {trusted_proxy} (v{trusted_proxy.version})"
|
||||||
|
)
|
||||||
|
if trusted_proxy.version == 4:
|
||||||
|
ipv4 = ip.ipv4_mapped if ip.version == 6 else ip
|
||||||
|
if ipv4 in trusted_proxy:
|
||||||
|
trusted = True
|
||||||
|
logger.debug(f"Trusted: {str(ip)} by {str(trusted_proxy)}")
|
||||||
|
break
|
||||||
|
elif trusted_proxy.version == 6 and ip.version == 6:
|
||||||
|
if ip in trusted_proxy:
|
||||||
|
trusted = True
|
||||||
|
logger.debug(f"Trusted: {str(ip)} by {str(trusted_proxy)}")
|
||||||
|
break
|
||||||
|
if trusted:
|
||||||
|
logger.debug(f"{ip} is trusted")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logger.debug(f"First untrusted IP: {str(ip)}")
|
||||||
|
return str(ip)
|
||||||
|
|
||||||
|
# if there wasn't anything in the route, just return the default
|
||||||
|
return request.remote_addr or "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
limiter = Limiter(
|
limiter = Limiter(
|
||||||
lambda: get_remote_address,
|
get_remote_addr,
|
||||||
storage_uri="memory://",
|
storage_uri="memory://",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -136,6 +136,10 @@ class AuthConfig(FrigateBaseModel):
|
|||||||
default="1/second;5/minute;20/hour",
|
default="1/second;5/minute;20/hour",
|
||||||
title="Rate limits for failed login attempts.",
|
title="Rate limits for failed login attempts.",
|
||||||
)
|
)
|
||||||
|
trusted_proxies: Optional[List[str]] = Field(
|
||||||
|
default=[],
|
||||||
|
title="Trusted proxies for determining IP address to rate limit",
|
||||||
|
)
|
||||||
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
||||||
hash_iterations: int = Field(default=600000, title="Password hash iterations")
|
hash_iterations: int = Field(default=600000, title="Password hash iterations")
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
|||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
const err = error as AxiosError;
|
const err = error as AxiosError;
|
||||||
if (err.response?.status === 429) {
|
if (err.response?.status === 429) {
|
||||||
toast.error("Exceeded rate limit. Try again in 1 minute.", {
|
toast.error("Exceeded rate limit. Try again later.", {
|
||||||
position: "top-center",
|
position: "top-center",
|
||||||
});
|
});
|
||||||
} else if (err.response?.status === 400) {
|
} else if (err.response?.status === 400) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user