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

  1. Test the bind outside RisingWave: ldapwhoami -H ldaps://host:636 -D 'bind_dn' -w 'password'
  2. Use the fully-qualified DN for bind_dn (e.g. cn=svc-rw,ou=service,dc=corp,dc=com), not a bare username
  3. Re-sync the bind password in config after any rotation
  4. 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

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


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/c7fa666d2796113b. Report an issue: GitHub.