{"record":{"id":"c7fa666d2796113b","repo":"risingwavelabs/risingwave","slug":"ldap-bind-as-search-user-failed","errorCode":null,"errorMessage":"LDAP bind as search user failed","messagePattern":"LDAP bind as search user failed","errorType":"exception","errorClass":"PsqlError","httpStatus":null,"severity":"error","filePath":"src/utils/pgwire/src/ldap_auth.rs","lineNumber":487,"sourceCode":"    async fn search_and_bind(&self, username: &str, password: &str) -> PsqlResult<bool> {\n        // Establish connection to LDAP server\n        let mut ldap = self.establish_connection().await?;\n\n        // Validate base_dn configuration\n        let base_dn = self\n            .config\n            .base_dn\n            .as_ref()\n            .ok_or_else(|| PsqlError::StartupError(\"LDAP base_dn not configured\".into()))?;\n\n        // If bind_dn and bind_passwd are provided, bind as that user first\n        if let (Some(bind_dn), Some(bind_passwd)) = (&self.config.bind_dn, &self.config.bind_passwd)\n        {\n            ldap.simple_bind(bind_dn, bind_passwd)\n                .await\n                .map_err(|e| {\n                    PsqlError::StartupError(\n                        anyhow!(e).context(\"LDAP bind as search user failed\").into(),\n                    )\n                })?\n                .success()\n                .map_err(|e| {\n                    PsqlError::StartupError(\n                        anyhow!(e).context(\"LDAP bind as search user failed\").into(),\n                    )\n                })?;\n        }\n\n        // Build search filter\n        let search_filter = if let Some(filter_template) = &self.config.search_filter {\n            // Use custom filter template with $username placeholder\n            // SECURITY: Escape username to prevent LDAP filter injection\n            let escaped_username = ldap_escape(username);\n            filter_template.replace(\"$username\", &escaped_username)\n        } else {\n            // Default filter using search_attribute (defaults to \"uid\" if not configured)","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/utils/pgwire/src/ldap_auth.rs#L469-L505","documentation":"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.","triggerScenarios":"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","commonSituations":"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.","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"],"exampleFix":"// before\nbind_dn = 'svc-risingwave'          // bare username, directory needs DN\n// after\nbind_dn = 'cn=svc-risingwave,ou=service-accounts,dc=corp,dc=com'","handlingStrategy":"try-catch","validationCode":"// verify search-account bind before wiring it into RisingWave\n# ldapwhoami -H ldaps://ldap.corp:636 -D 'cn=svc-rw,ou=service,dc=corp,dc=com' -w \"$BIND_PASS\"","typeGuard":null,"tryCatchPattern":"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)","preventionTips":["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"],"tags":["ldap","authentication","bind","credentials"],"backgroundTag":"authentication-required","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}