mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-31 16:15:58 +00:00
Fixhancement: PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT (#13281)
This commit is contained in:
+21
-7
@@ -522,22 +522,36 @@ do CORS calls. Set this to your public domain name.
|
||||
fail2ban with log entries for failed authorization attempts. Value should be
|
||||
IP address(es).
|
||||
|
||||
This setting also controls allauth's
|
||||
[`ALLAUTH_TRUSTED_PROXY_COUNT`](https://docs.allauth.org/en/latest/account/configuration.html),
|
||||
which is set to the number of proxies listed here. Without this,
|
||||
allauth cannot determine the client IP address for rate limiting when
|
||||
running behind a reverse proxy, resulting in a `403 Forbidden` on login.
|
||||
By default, this setting also controls allauth's trusted proxy count,
|
||||
which is set to the number of proxies listed here. Override that default
|
||||
with [`PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT`](#PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT)
|
||||
when the list length does not match the number of proxy hops.
|
||||
|
||||
Defaults to empty string.
|
||||
|
||||
#### [`PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT=<integer>`](#PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT) {#PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT}
|
||||
|
||||
: Sets allauth's
|
||||
[`ALLAUTH_TRUSTED_PROXY_COUNT`](https://docs.allauth.org/en/latest/common/rate_limits.html#configuration).
|
||||
This is the number of trusted proxy **hops** represented in each
|
||||
`X-Forwarded-For` header, not the number of IP addresses through which those
|
||||
proxies may be reached. For example, a single dual-stack proxy is one hop even
|
||||
when its IPv4 and IPv6 addresses are both listed in
|
||||
[`PAPERLESS_TRUSTED_PROXIES`](#PAPERLESS_TRUSTED_PROXIES).
|
||||
|
||||
Only trust `X-Forwarded-For` when untrusted clients cannot connect directly
|
||||
to Paperless-ngx.
|
||||
|
||||
Defaults to the number of entries in `PAPERLESS_TRUSTED_PROXIES`.
|
||||
|
||||
#### [`PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER=<header-name>`](#PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER) {#PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER}
|
||||
|
||||
: Sets allauth's
|
||||
[`ALLAUTH_TRUSTED_CLIENT_IP_HEADER`](https://docs.allauth.org/en/latest/account/configuration.html).
|
||||
[`ALLAUTH_TRUSTED_CLIENT_IP_HEADER`](https://docs.allauth.org/en/latest/common/rate_limits.html#configuration).
|
||||
Use this when your reverse proxy sets a dedicated header for the real
|
||||
client IP instead of `X-Forwarded-For`, for example `X-Real-IP` (nginx)
|
||||
or `CF-Connecting-IP` (Cloudflare). When set, this takes precedence over
|
||||
[`PAPERLESS_TRUSTED_PROXIES`](#PAPERLESS_TRUSTED_PROXIES).
|
||||
`PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT`.
|
||||
|
||||
Defaults to none.
|
||||
|
||||
|
||||
@@ -324,8 +324,11 @@ option since v1.8.0.
|
||||
Allauth changed how it determines the client IP address for login rate limiting. Users running
|
||||
behind a reverse proxy may need to set
|
||||
[`PAPERLESS_TRUSTED_PROXIES`](configuration.md#PAPERLESS_TRUSTED_PROXIES),
|
||||
[`PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT`](configuration.md#PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT),
|
||||
[`PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER`](configuration.md#PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER),
|
||||
or both, to avoid `403 Forbidden` errors on login.
|
||||
or a combination of these settings to avoid `403 Forbidden` errors on login.
|
||||
The proxy count is the number of proxy hops in `X-Forwarded-For`, which may
|
||||
differ from the number of configured proxy IP addresses.
|
||||
|
||||
## Minimum CPU Requirements (NumPy Baseline)
|
||||
|
||||
|
||||
@@ -460,11 +460,22 @@ def _parse_paperless_url():
|
||||
|
||||
PAPERLESS_URL = _parse_paperless_url()
|
||||
|
||||
|
||||
def _get_allauth_trusted_proxy_count(trusted_proxies: list[str]) -> int:
|
||||
count = get_int_from_env(
|
||||
"PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT",
|
||||
len(trusted_proxies),
|
||||
)
|
||||
if count < 0:
|
||||
raise ImproperlyConfigured(
|
||||
"PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT must be zero or greater",
|
||||
)
|
||||
return count
|
||||
|
||||
|
||||
# For use with trusted proxies
|
||||
TRUSTED_PROXIES = get_list_from_env("PAPERLESS_TRUSTED_PROXIES")
|
||||
# Derive allauth's proxy count from the same list so X-Forwarded-For is trusted
|
||||
# correctly when users have configured PAPERLESS_TRUSTED_PROXIES.
|
||||
ALLAUTH_TRUSTED_PROXY_COUNT = len(TRUSTED_PROXIES)
|
||||
ALLAUTH_TRUSTED_PROXY_COUNT = _get_allauth_trusted_proxy_count(TRUSTED_PROXIES)
|
||||
ALLAUTH_TRUSTED_CLIENT_IP_HEADER = os.getenv(
|
||||
"PAPERLESS_ALLAUTH_TRUSTED_CLIENT_IP_HEADER",
|
||||
)
|
||||
|
||||
@@ -3,7 +3,9 @@ from unittest import TestCase
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from paperless.settings import _get_allauth_trusted_proxy_count
|
||||
from paperless.settings import _get_search_language_setting
|
||||
from paperless.settings import _parse_paperless_url
|
||||
from paperless.settings import default_threads_per_worker
|
||||
@@ -35,6 +37,31 @@ class TestThreadCalculation(TestCase):
|
||||
self.assertLessEqual(default_workers * default_threads, i)
|
||||
|
||||
|
||||
def test_allauth_trusted_proxy_count_defaults_to_trusted_proxies(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.delenv("PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT", raising=False)
|
||||
|
||||
assert _get_allauth_trusted_proxy_count(["proxy-v4", "proxy-v6"]) == 2
|
||||
|
||||
|
||||
def test_allauth_trusted_proxy_count_can_be_overridden(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT", "1")
|
||||
|
||||
assert _get_allauth_trusted_proxy_count(["proxy-v4", "proxy-v6"]) == 1
|
||||
|
||||
|
||||
def test_allauth_trusted_proxy_count_rejects_negative_values(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("PAPERLESS_ALLAUTH_TRUSTED_PROXY_COUNT", "-1")
|
||||
|
||||
with pytest.raises(ImproperlyConfigured, match="must be zero or greater"):
|
||||
_get_allauth_trusted_proxy_count([])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("env_value", "expected"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user