From 9923eefcab13ae8da3214585c071cd8bef1e74e2 Mon Sep 17 00:00:00 2001 From: Ashesh Vashi Date: Mon, 4 May 2026 12:39:12 +0530 Subject: [PATCH] docs: clarify scope of MAX_LOGIN_ATTEMPTS and document brute-force protection for LDAP The "Avoiding a bruteforce attack" section in login.rst implied that MAX_LOGIN_ATTEMPTS protected all logins; in fact it only applies to the INTERNAL authentication source (the /authenticate/login view filters by auth_source=INTERNAL). Operators using LDAP / OAUTH2 / KERBEROS / WEBSERVER got no signal that brute-force protection lives at a different layer. Adds: - login.rst: the bruteforce section now states that MAX_LOGIN_ATTEMPTS is INTERNAL-only and points operators of external sources to the upstream identity provider's lockout policy and to the reverse proxy for IP-based throttling. - ldap.rst: a new "Brute-force protection" section calling out that LDAP credential lockout is the directory's responsibility (ppolicy / AD account-lockout GPO) and that request rate-limiting belongs on the proxy. Cross-references the login-page section. No code change. Closes the documentation gap that surfaced during the analysis of the #9904 lockout-bypass fix without inheriting the maintenance burden of duplicating directory lockout in pgAdmin. --- docs/en_US/ldap.rst | 25 +++++++++++++++++++++++++ docs/en_US/login.rst | 18 +++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/docs/en_US/ldap.rst b/docs/en_US/ldap.rst index b8f598d62..ff1ae9313 100644 --- a/docs/en_US/ldap.rst +++ b/docs/en_US/ldap.rst @@ -110,3 +110,28 @@ There are 3 ways to configure LDAP: "LDAP_BIND_PASSWORD", "Password for simple bind. Specify the value if you have set the LDAP_BIND_USER parameter." + +Brute-force protection +********************** + +pgAdmin's built-in account-lockout setting (``MAX_LOGIN_ATTEMPTS``) applies +only to accounts using the ``INTERNAL`` authentication source. It does +**not** rate-limit or lock LDAP-authenticated logins. + +When LDAP authentication is enabled, brute-force protection for LDAP +credentials is the responsibility of: + +- **The directory.** Configure account-lockout policy on the LDAP server + itself (for example, OpenLDAP's ``ppolicy`` overlay, Active Directory's + account-lockout GPO, or the equivalent on FreeIPA / 389 Directory + Server). The directory is the credential authority and is the correct + place to enforce per-account lockout. +- **The reverse proxy or WAF in front of pgAdmin.** Configure + request-rate limiting on the login endpoint (for example, ``limit_req`` + in nginx, ``stick-table`` rules in HAProxy, or the equivalent in your + WAF) to bound login-attempt volume per source IP. This is independent + of pgAdmin and protects against attackers who would otherwise reach the + LDAP server through pgAdmin. + +See :ref:`Avoiding a bruteforce attack ` for the equivalent note +on the login page. \ No newline at end of file diff --git a/docs/en_US/login.rst b/docs/en_US/login.rst index 798b3de4e..fcd303d51 100644 --- a/docs/en_US/login.rst +++ b/docs/en_US/login.rst @@ -60,6 +60,18 @@ asking you to contact the LDAP administrator to recover your LDAP password. Avoiding a bruteforce attack **************************** -You have the possibility to lock an account by setting ``MAX_LOGIN_ATTEMPTS`` -once it has reached the maximum number of login attempts. -You can disable this feature by setting the value to zero. +For accounts using the ``INTERNAL`` authentication source, pgAdmin locks +the account after ``MAX_LOGIN_ATTEMPTS`` consecutive failed logins. The +default is 3; set it to zero to disable. + +For accounts authenticated against an external identity provider +(``LDAP``, ``OAUTH2``, ``KERBEROS``, ``WEBSERVER``), brute-force protection +is **not** handled by pgAdmin and ``MAX_LOGIN_ATTEMPTS`` does not apply. +Operators using these sources should: + +- Configure account-lockout policy on the upstream provider (for example, + Active Directory's account-lockout GPO, OpenLDAP's ``ppolicy`` overlay, + or the OAuth2 provider's rate limits). +- Configure request rate-limiting (for example, ``limit_req`` in nginx) + on the reverse proxy in front of pgAdmin to bound login-attempt volume + per source IP.