XMPP channel binding: serve auth from ejabberd's SQL store with generated passwords #690
Open
opened 2026-10-30 06:23:01 +00:00 by raucao
·
0 comments
No Branch/Tag Specified
master
monitoring/filesystem_out_of_space
chore/upgrade_gitea_to_1.27.1
bugfix/substr_url_matching
notes/ejabberd_ldap_photo
feature/akaunting
chore/ejabberd_service_avatar
feature/237-gitea_gpg
No results found.
Labels
Clear labels
monitoring
bug
design
dev environment
docs
duplicate
enhancement
feature
good first issue
idea
invalid
kredits-1
kredits-2
kredits-3
on hold
ops
question
security
ui/ux
wontfix
Infrastructure metrics, alerts, notifications, etc.
service
discourse
Kosmos Community Forums
service
accounts
Kosmos Accounts
service
drone-ci
Kosmos Drone CI
service
email
mail.kosmos.org
service
garage
S3-compatible object storage
service
gitea
Kosmos Gitea
service
ipfs
Kosmos IPFS
service
lightning
Lightning address, lndhub, etc.
service
mastodon
kosmos.social
service
nostr
Relays, Blossom server, etc.
service
postgres
Database cluster
service
remotestorage
Portable data storage for the Web
service
wiki
Kosmos Wiki
service
xmpp
Kosmos Chat
Something is not working
Graphic/visual design
Config, builds, CI, deployment, etc.
Documentation
This issue or pull request already exists
Improving existing functionality
New functionality
Dive in, and start contributing
Something to consider
Not a bug
Small contribution
Medium contribution
Large contribution
Currently not actionable
Manual IT ops activities
Looking for an answer
All your base are belong to us
User interface, process design, etc.
This won't be fixed
release
major
release
minor
release
patch
Milestone
No items
No Milestone
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: kosmos/chef#690
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
Conversations (Android) 2.21.0 now requires channel binding by default for new installs. Our ejabberd authenticates against LDAP (
auth_method: ldapper vhost insite-cookbooks/kosmos-ejabberd/templates/vhost.yml.erb), and LDAP cannot provide channel binding.Goal: make
kosmos.orgadvertiseSCRAM-SHA-256-PLUSso channel binding works, while keeping LDAP as the source of truth for account existence and attributes.Why LDAP can't do channel binding
ejabberd_auth_ldaponly does an LDAP bind:store_type/1 -> external,plain_password_required/1 -> true.SCRAM
-PLUSrequires the SCRAM verifier (storedkey/serverkey/salt/iterations) in ejabberd's own store (mnesia/SQL).ejabberd_auth:store_type/1collapses toexternalif any module is LDAP, andejabberd_c2s:sasl_mechanisms/2derives the advertised mechanism list from that single host-level value:So
auth_method: [sql, ldap]yieldsstore_type = external⇒ no SCRAM at all (PLAIN only) for everyone, including users who have a verifier in SQL. The mechanism list is per host, not per user. ⇒kosmos.orgmust useauth_method: sqlonly.Existing LDAP hashes cannot be reused: akkounts stores
{SSHA512}base64(SHA512(password+salt)+salt), a one-way digest that cannot be turned into a SCRAM verifier.Chosen approach
Each account gets its own generated XMPP password (analogous to the existing email password) which is pushed into ejabberd's SQL store. LDAP remains the source of truth via a marker attribute.
Decisions
[sql]forkosmos.org.kosmos.orgonly.5apps.comstays on LDAP (no channel binding there).unregisteronly on account deletion.Changes
akkounts (
kosmos/akkounts)schemas/ldap/xmpp_password.ldif(new OID under1.3.6.1.4.1.61554…), add tolib/tasks/ldap.rakeadd/delete lists and the ACItargetattrinLdapService#add_organization, add toLdapService#fetch_users. NewLdapManager::UpdateXmppPassword. Marker only — store a bcrypt hash/timestamp, never plaintext (the verifier can't be read back).EjabberdManagerService#change_password(user, password)(POSTchange_password) +EjabberdManager::SetPassword; treat non-200/error results as failures._email.html.erb), one-timenew_xmpp_passwordview via session, and updated Services → Chat copy pointing users at the new password.XmppSetPasswordJobwith retries on enable; ondisable_service(:ejabberd)push a random password;unregisteron account deletion.Setting.xmpp_password_enabled?from an env var) so it can't run — and can't clobber LDAP passwords — before ejabberd is on SQL.EjabberdManager::SetPasswordservice spec (WebMock), settings feature spec, LDAP service spec.chef (this repo)
site-cookbooks/kosmos-ejabberd/recipes/default.rb+templates/vhost.yml.erb: add a per-hostauth_method;kosmos.org=sql,5apps.com=ldap(only renderldap_*when LDAP is used).site-cookbooks/kosmos-ejabberd/templates/ejabberd.yml.erb: addchange_password,unregister,check_accountto the akkountsapi_permissionsallowlist; setauth_scram_hash: sha256.change_passwordfor each service account (idempotent, re-run on config changes).doc/ejabberd.mdand the InSpec test.Hard-cutover runbook
kosmos.org→auth_method: sql, add allowlist entries,auth_scram_hash: sha256, and the bot push. (Human XMPP logins onkosmos.orgwill fail until users generate a password.)serviceEnabled=ejabberdagainstregistered_usersonkosmos.org.auth_method: ldap. Safe only if step 1's flag keptchange_passwordaway from LDAP — otherwise generated passwords would have overwritten real LDAP passwords.Risks / notes
change_passwordwrites to LDAP (eldap_pool:modify_passwd). The dark-launch flag in step 1 is what makes the hard cutover safe. (set_passwordtries auth methods in configured order and stops at the first success, so if methods are ever combined, SQL must come first — with LDAP first it would overwrite LDAP passwords.)auth_scram_hash: sha256before users generate passwords; changing it later invalidates them.[sql], the LDAPserviceEnabledgate no longer governs XMPP — akkounts becomes the provisioning authority (enable/disable/delete must map to push/random-password/unregister).5apps.comstays on LDAP and therefore still has no channel binding; revisit if its users rely on Conversations.usersschema v2 (server_host,type) is auto-migrated viaupdate_sql_schema: true.Open item
Enumerate all non-human XMPP accounts so the chef push and the audit cover every one. Only
hal8000(HUBOT_XMPP_PASSWORDin thehubotdata bag) is explicitly visible; check for botka, sockethub, bridge accounts, etc.XMPP channel binding: serve kosmos.org auth from ejabberd's SQL store with generated passwordsto XMPP channel binding: serve auth from ejabberd's SQL store with generated passwords