Skip to content

Commit 363b638

Browse files
committed
docs(auth): update LDAP config description with admin account security warning
1 parent bb9efa9 commit 363b638

5 files changed

Lines changed: 28 additions & 15 deletions

File tree

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ services:
6767
GRAPHQL_PORT: ${GRAPHQL_PORT:-20212} # GraphQL API port
6868
ALWAYS_FRESH_INSTALL: ${ALWAYS_FRESH_INSTALL:-false} # Set to true to reset your config and database on each container start
6969
NETALERTX_DEBUG: ${NETALERTX_DEBUG:-0} # 0=kill all services and restart if any dies. 1 keeps running dead services.
70+
# LDAP Authentication Variables
71+
LDAP_ENABLED: ${LDAP_ENABLED:-}
72+
LDAP_SERVER: ${LDAP_SERVER:-} # LDAP server hostname or IP
73+
LDAP_PORT: ${LDAP_PORT:-389}
74+
LDAP_USE_SSL: ${LDAP_USE_SSL:-false}
75+
LDAP_USE_START_TLS: ${LDAP_USE_START_TLS:-false}
76+
LDAP_TLS_VERIFY_CERT: ${LDAP_TLS_VERIFY_CERT:-true}
77+
LDAP_CA_CERT_PATH: ${LDAP_CA_CERT_PATH:-}
78+
LDAP_DISABLE_LOCAL_ADMIN: ${LDAP_DISABLE_LOCAL_ADMIN:-false}
79+
LDAP_DIRECT_BIND_FORMAT: ${LDAP_DIRECT_BIND_FORMAT:-}
80+
LDAP_BIND_DN: ${LDAP_BIND_DN:-}
81+
LDAP_BIND_PASSWORD: ${LDAP_BIND_PASSWORD:-} # Service account password for LDAP
82+
LDAP_BASE_DN: ${LDAP_BASE_DN:-}
83+
LDAP_USER_FILTER: ${LDAP_USER_FILTER:-(uid={username})}
84+
LDAP_USERNAME_ATTRIBUTE: ${LDAP_USERNAME_ATTRIBUTE:-uid}
7085

7186
# Resource limits to prevent resource exhaustion
7287
mem_limit: 2048m # Maximum memory usage

front/php/templates/security.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@ function getConfigLine($pattern, $config_lines) {
2626
return !empty($matches) ? explode("=", array_values($matches)[0]) : null;
2727
}
2828

29-
function getConfigValue($pattern, $config_lines) {
29+
function getConfigValue($pattern, $config_lines, $delimiter = "'") {
3030
$line = preg_grep($pattern, $config_lines);
31-
if (empty($line)) return '';
32-
$val = explode('=', array_values($line)[0], 2);
33-
if (!isset($val[1])) return '';
34-
return trim(trim($val[1]), "\"'");
31+
return !empty($line) ? explode($delimiter, array_values($line)[0])[1] : '';
3532
}
3633

3734
function redirect($url) {
@@ -96,7 +93,7 @@ function redirect($url) {
9693
$nax_WebProtection = 'true';
9794
}
9895
$nax_Password = getConfigValue('/^SETPWD_password\s*=/', $configLines);
99-
$api_token = getConfigValue('/^API_TOKEN\s*=/', $configLines);
96+
$api_token = getConfigValue('/^API_TOKEN\s*=/', $configLines, "'");
10097
if (empty($api_token)) {
10198
$api_token = getenv('API_TOKEN') ?: '';
10299
}

front/plugins/auth_ldap/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@
202202
"description": [
203203
{
204204
"language_code": "en_us",
205-
"string": "Upgrade a plain-text LDAP connection to TLS using the STARTTLS command. Typically used with port 389. Mutually exclusive with LDAPS.<br><br><b>Recommended: set to true (or use LDAP_USE_SSL=true with port 636) before production.</b>"
205+
"string": "Upgrade a plain-text LDAP connection to TLS using the STARTTLS command. Typically used with port 389. Mutually exclusive with LDAPS."
206206
}
207207
]
208208
},
@@ -304,7 +304,7 @@
304304
"description": [
305305
{
306306
"language_code": "en_us",
307-
"string": "When enabled, local admin login is completely disabled — only LDAP users can authenticate. <b>Recommended</b> for strict LDAP enforcement. <br><br><span style='color: #cc0000;'>Warning: If disabled (default), a compromised or unreachable LDAP server may allow attackers to fall back to the local password. However, enabling this risks lockout if the LDAP server goes down.</span>"
307+
"string": "When enabled, local admin login is completely disabled — only LDAP users can authenticate. <b>Recommended</b> for strict LDAP enforcement. <br><br><span style='color: #cc0000;'>Warning: If disabled (default), the local admin account with username \"admin\" (default password \"123456\", configurable via \"Set Password->SETPWD_password\") remains active and accessible via the LDAP login form. This can be used as a fallback during LDAP setup, but it also creates a significant security risk if the default password is not changed or if a compromised/unreachable LDAP server allows attackers to fall back to this local account. Enabling this feature overrides the `SETPWD_enable_password` setting.</span>"
308308
}
309309
]
310310
},

server/api_server/api_server_start.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,16 +1991,15 @@ def _get_client_ip():
19911991
def api_auth_login(payload=None):
19921992
"""Authenticate a user and return provider + username on success."""
19931993
data = payload
1994-
# For internal admin, there is no username on the login page, so we default to 'admin'
1995-
username = data.username if data and data.username else "admin"
1994+
username = data.username if data else ""
19961995
password = data.password if data else ""
19971996
client_ip = _get_client_ip()
19981997

1999-
if not password:
1998+
if not username or not password:
20001999
return jsonify({
20012000
"success": False,
20022001
"message": "Missing credentials",
2003-
"error": "password is required",
2002+
"error": "username and password are required",
20042003
}), 400
20052004

20062005
# Rate limiting check (thread-safe)
@@ -2018,6 +2017,7 @@ def api_auth_login(payload=None):
20182017
attempts, last_attempt = FAILED_LOGINS[client_ip]
20192018
if attempts >= max_attempts:
20202019
if now - last_attempt < lockout_time:
2020+
from auth.ldap_provider import _sanitize_for_log
20212021
write_notification(
20222022
f"[auth] Rate limit exceeded for IP {client_ip} trying to log in as '{_sanitize_for_log(username)}'",
20232023
"alert",

server/auth/manager.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ def authenticate(self, username: str, password: str) -> AuthResult:
5252
# If the user doesn't exist in LDAP, we can fallback to local auth
5353
if ldap_result.error == LdapProvider.USER_NOT_FOUND:
5454
if not disable_local:
55-
# Fallback decision is delegated to the LocalProvider policy
56-
if not LocalProvider.is_fallback_allowed(username):
57-
mylog("verbose", [f"[auth.manager] Local fallback denied for non-recovery user '{_sanitize_for_log(username)}'"])
55+
# Only the built-in local admin is a recovery account;
56+
# all other identities must exist in LDAP.
57+
if username != "admin":
58+
mylog("verbose", [f"[auth.manager] Local fallback denied for non-admin user '{_sanitize_for_log(username)}'"])
5859
return ldap_result
5960
mylog("warning", ["[auth.manager] User not found in LDAP, falling back to local provider"])
6061
local_result = LocalProvider().authenticate(username, password)

0 commit comments

Comments
 (0)