risingwavelabs/risingwave · error · PsqlError
LDAP bind as search user failed
Error message
LDAP bind as search user failed
What it means
In search_and_bind mode, RisingWave first binds to the LDAP directory using the configured bind_dn/bind_passwd (the service/search account). Both the simple_bind future and the subsequent .success() check map any failure to this StartupError. It means the search-phase credentials were rejected or the bind operation errored.
Source
Thrown at src/utils/pgwire/src/ldap_auth.rs:487
async fn search_and_bind(&self, username: &str, password: &str) -> PsqlResult<bool> {
// Establish connection to LDAP server
let mut ldap = self.establish_connection().await?;
// Validate base_dn configuration
let base_dn = self
.config
.base_dn
.as_ref()
.ok_or_else(|| PsqlError::StartupError("LDAP base_dn not configured".into()))?;
// If bind_dn and bind_passwd are provided, bind as that user first
if let (Some(bind_dn), Some(bind_passwd)) = (&self.config.bind_dn, &self.config.bind_passwd)
{
ldap.simple_bind(bind_dn, bind_passwd)
.await
.map_err(|e| {
PsqlError::StartupError(
anyhow!(e).context("LDAP bind as search user failed").into(),
)
})?
.success()
.map_err(|e| {
PsqlError::StartupError(
anyhow!(e).context("LDAP bind as search user failed").into(),
)
})?;
}
// Build search filter
let search_filter = if let Some(filter_template) = &self.config.search_filter {
// Use custom filter template with $username placeholder
// SECURITY: Escape username to prevent LDAP filter injection
let escaped_username = ldap_escape(username);
filter_template.replace("$username", &escaped_username)
} else {
// Default filter using search_attribute (defaults to "uid" if not configured)View on GitHub (pinned to 6469eb736d)
Solutions
- Test the bind outside RisingWave: ldapwhoami -H ldaps://host:636 -D 'bind_dn' -w 'password'
- Use the fully-qualified DN for bind_dn (e.g. cn=svc-rw,ou=service,dc=corp,dc=com), not a bare username
- Re-sync the bind password in config after any rotation
- Confirm the bind account is active/unlocked in the directory
Example fix
// before bind_dn = 'svc-risingwave' // bare username, directory needs DN // after bind_dn = 'cn=svc-risingwave,ou=service-accounts,dc=corp,dc=com'
Defensive patterns
Strategy: try-catch
Validate before calling
// verify search-account bind before wiring it into RisingWave # ldapwhoami -H ldaps://ldap.corp:636 -D 'cn=svc-rw,ou=service,dc=corp,dc=com' -w "$BIND_PASS"
Try / catch
catch PsqlError::StartupError with context 'LDAP bind as search user failed'; log the bind_dn (never the password) and point to the LDAP result code to distinguish bad credentials (49) from policy errors (53)
Prevention
- Store bind credentials in a secret manager and rotate them together with the directory
- Always use the full DN of the service account, never a bare username
- Run a periodic canary bind check against the directory
- Set a long-lived password policy / no-expiry for the service account
When it happens
Trigger: ldap.simple_bind(bind_dn, bind_passwd).await returns Err, or the returned LdapResult .success() is Err — invalid credentials for bind_dn, unknown DN, or protocol-level error
Common situations: Typo in bind_dn (wrong OU ordering, missing dc components); bind account password rotated or expired; bind_dn uses a plain username instead of a full DN when the directory requires a DN; bind account locked by password policy.
Related errors
- LDAP bind failed
- Both `access_key` and `secret_key` must be provided
- No auth method specified for Vault backend
- Vault app role login failed: {} - {}
- NATS connect mode `user_and_password` requires both `user` a
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/c7fa666d2796113b.
Report an issue: GitHub.