====== Local Jabber/XMPP server with ejabberd and Debian ====== Jabber, the XMPP instant messenging protocol, is now the standard for IM communications. Slowly kicking out monstrosities from the 90's (msn, aol and such). The enormous advantage of Jabber is its decentralized architecture. Thus, you can setup you own server at home, with your own domain name, and communicate with any other xmpp user over the world (jabber.org, gtalk, ...). Right. That's the big commercial picture. Now, to build that, you need a little bit of a setup. Say: * a jabber server, such as ejabberd * a DNS server (bind) * a LDAP server (optionnal, but fancy) * a xmpp client (psi, mcabber, ...) ===== XMPP Basis ===== {{:en:ressources:articles:xmpp.png|}} , formely called jabber (but don't get confused, it's the same thing) is a network protocol standardized in RFCs [[http://tools.ietf.org/html/rfc3920|3920]] and [[http://tools.ietf.org/html/rfc3921|3921]]. It is absolutely open and free (as in freedom). In the XMPP world, users are identified by their JID. It's form is identical to the email: [username]@[location]. That means that you can have a JID that is identical to your email address: jean-kevin@mydomain.net. Each XMPP server controls (or participate to) a domain and is responsible for contacting other domains. The XMPP server will send and receive messages from and to its users, but will also exchange presence datas to keep the list of connected users up to date (actually, this represents more than 60% of the XMPP traffic). It will also manage conference rooms, store profiles, queue connection requests, and so on... ===== ejabberd ===== Erlang Jabber Daemon, ejabberd, is probably the most popular xmpp server on Linux systems. The Debian version is very well packaged, so on Debian Lenny, a simple apt-get is enough to install it: # apt-get install ejabberd The configuration is located at **/etc/ejabberd/**. The main configuration file is **ejabberd.cfg**. The file itself is pretty well documented and almost ready for use. We just need to specify our domain name and provide a SSL certificate for STARTTLS communications between clients and the server (c2s) and server to server (s2s). Some servers, such as the ones from Gtalk, impose the use of STARTTLS in s2s communications. ==== Domain configuration ==== To configure your domain name in ejabberd.cfg, change the **hosts** directive: %% Hostnames {hosts, ["mydomain.net"]}. ejabberd configuration file is written in erlang syntax, where the % % signs represent a comment, and each directive **must** end with a dot. If you want to manage several domains with the same server, list them in the same **hosts** directive: {hosts, ["mydomain.net", "mydomain.org"]}. But keep in mind that each domain, or subdomain, has its set of independent users. ==== TLS Listener ==== ejabberd has typically two listener: one for c2s communications listening on port 5222, and one for s2s communication listening on port 5269. Each listener can furnish a STARTTLS support to enable TLS communications over the wire. But, for that, ejabberd needs a SSL certificate. Debian packages a self-signed certificate that's deposed in **/etc/ejabberd/ejabberd.pem**. Let's change that. Generate your own certificate and private key and create a folder called **certs** in ejabberd folder. There, deposit you signed certificate (a PEM file) and the private key, as follow: root@server:/etc/ejabberd/certs # ls -l total 20 -rw-r--r-- 1 root root 1675 aoû 13 12:58 jabber.mydomain.net.key -rw-r--r-- 1 root root 7077 aoû 13 12:58 jabber.mydomain.net.pem and back in **ejabberd.cfg**, change the c2s listener and s2s certfile: 114 {listen, 115 [ 116 {5222, ejabberd_c2s, [ 117 {access, c2s}, 118 {shaper, c2s_shaper}, 119 {max_stanza_size, 65536}, 120 starttls, {certfile, "/etc/ejabberd/certs/jabber.mydomain.net.pem"} 121 ]}, [...] 206 {s2s_certfile, "/etc/ejabberd/certs/jabber.mydomain.net.pem"}. Basic configuration of the server is done, you can restart it and verify that port 5222 and 5269 are listening to incoming TCP connections. # /etc/init.d/ejabberd restart Restarting jabber server: ejabberd. # netstat -taupen |grep -E "5222|5269" tcp 0 0 0.0.0.0:5222 0.0.0.0:* LISTEN 119 21657632 10743/beam tcp 0 0 0.0.0.0:5269 0.0.0.0:* LISTEN 119 21657634 10743/beam ==== Register a user ==== **ejabberdctl** is a program furnished with the ejabberd package to provide realtime control over the server while it's running. ejabberctl provides a large set of functions, so feel free to browse the documentation (or simply run **ejabberdctl -h** and play with the commands). To register a new user to the server, use the following command: ejabberdctl register . Exemple with jean-kevin: # ejabberdctl register jean-kevin mydomain.net 'm3g4password' jean-kevin can now log in. But that's pretty much all jean-kevin can do, because we have to let the outside world know about our xmpp server, and this is going to be done with DNS SRV records. ===== Configure the DNS to provide XMPP information ===== A XMPP server connected to the internet will be able to contact other XMPP servers almost right away. But for another server to contact us, we need to publish some information through the DNS master of the domain. Imagine that michel@toto.com wants to talk to jean-kevin@mydomain.net. The cinematic is as follow: - michel's client connect to its own xmpp server (called SRVMICHEL) and sends a message destinated to jean-kevin (MESSAGE1) - SRVMICHEL receives MESSAGE1 and look more closely at the receiver's address: jean-kevin@mydomain.net. SRVMICHEL needs to determine what server is in charge of XMPP messages for mydomain.net. So, SRVMICHEL performs a DNS query similar to the following one: $ dig SRV _xmpp-server._tcp.mydomain.net [...] ;; QUESTION SECTION: ;_xmpp-server._tcp.mydomain.net. IN SRV ;; ANSWER SECTION: _xmpp-server._tcp.mydomain.net. 259200 IN SRV 5 0 5269 jabber.mydomain.net. ;; ADDITIONAL SECTION: jabber.mydomain.net. 259200 IN A 11.22.33.44 ;; Query time: 109 msec ;; SERVER: 212.27.40.241#53(212.27.40.241) ;; WHEN: Sat Aug 14 14:14:22 2010 ;; MSG SIZE rcvd: 111 The query ask for the **xmpp-server** in charge of the domain mydomain.net. The DNS of mydomain.net replies that this xmpp is handled by jabber.mydomain.net on tcp port 5269, and that jabber.mydomain.net is located at the IP address 11.22.33.44. - SRVMICHEL then connects to ip address 11.22.33.44 on TCP port 5269 - SRVJEANKEVIN accepts the connection. Then follow an exchange of XMPP messages (signaling, data and so on) and SRVMICHEL sends MESSAGE1 to SRVJEANKEVIN. - SRVJEANKEVIN forward the MESSAGE1 to the client software used by Jean-kevin. So, what we need in our DNS master is a **SRV records** that publishes the XMPP information of mydomain.net to the Internet. In your DNS zone file, we add the following information: jabber IN A 11.22.33.44 conference IN CNAME jabber ;srvce.prot.name class rr pri weight port target _jabber._tcp.mydomain.net. IN SRV 5 0 5269 jabber.mydomain.net. _xmpp-server._tcp.mydomain.net. IN SRV 5 0 5269 jabber.mydomain.net. _xmpp-client._tcp.mydomain.net. IN SRV 5 0 5222 jabber.mydomain.net. Some explanations: * **conference** points to our jabber server and will be used for public chatrooms. * All SRV records have a priority of 5, a weight of 0 and point to jabber.mydomain.net. * **_jabber._tcp.mydomain.net.** is the legacy SRV record for jabber servers * **_xmpp-server._tcp.mydomain.net.** is the record that will be queried by other xmpp servers to know where to send xmpp messages * **_xmpp-client._tcp.mydomain.net.** is used by xmpp clients softwares to know to which server to connect to when adding a user account * As said before, server requests are sent to TCP port 5269 and client request to TCP port 5222 With these information, the DNS master can be restarted and you can test your configuration with a dig query similar to the one used previously. ===== Test first configuration ===== Now, if your server configuration, dns configuration, and eventually firewall rules and NAT are set properly, you should be able to connect to your XMPP server with any xmpp client and contact other XMPP users. For basic test, I recommend mcabber, a console xmpp client, locally on the ejabberd server. Install it with a quick apt-get and create a **~/.mcabber/mcabberrc** basic configuration file as follow: set username = jean-kevin@mydomain.net set server = localhost set port = 5222 set resource = mcabber set ignore_self_presence = 1 set ssl = 0 set cmdhistory_lines = 250 set message_autoaway = Auto-away (idle) set escdelay = 50 alias me = say /me alias online = status online alias away = status away alias dnd = status dnd alias notavail = status notavail bind 17 = roster unread_next bind 24 = roster alternate bind 269 = roster toggle_offline bind 276 = roster toggle bind 521 = buffer up bind 514 = buffer down Launch mcabber and type your password, you should see the following screen. try the command **/add michel@toto.com** to add a new user to your friend list: $ mcabber MCabber 0.9.7 -- Email: mcabber [at] lilotux [dot] net [14:43:14] MCabber 0.9.7 -- Email: mcabber [at] lilotux [dot] net [14:43:14] Reading /home/jeankevin/.mcabber/mcabberrc Server: localhost Username: jean-kevin@mydomain.net Please enter Jabber password: {{:en:ressources:articles:mcabber.png|}} ===== LDAP configuration ===== On my own configuration, users are stored in a LDAP directory. The tree is pretty basic and [[http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:openldap:openldap_debian|described in this article]]. Users are stored in **ou=people,dc=mydomain,dc=net**. ejabberd can easily query the LDAP directory to check user's passwords. To do so, you need to change the authentication backend in **ejabberd.cfg** configuration file. Comment the basic authentication backend and uncomment the lines concerning LDAP, as follow: 237 %%{auth_method, internal}. [...] 258 %% 259 %% Authentication using LDAP 260 %% 261 {auth_method, ldap}. 262 %% 263 %% List of LDAP servers: 264 {ldap_servers, ["localhost"]}. 265 %% 266 %% Encryption of connection to LDAP servers (LDAPS): 267 %%{ldap_encrypt, tls}. 268 %% 269 %% Port connect to LDAP server: 270 {ldap_port, 389}. 271 %% 272 %% LDAP manager: 273 %%{ldap_rootdn, "dc=example,dc=com"}. 274 %% 275 %% Password to LDAP manager: 276 %%{ldap_password, "******"}. 277 %% 278 %% Search base of LDAP directory: 279 {ldap_base, "ou=people,dc=mydomain,dc=net"}. 280 %% 281 %% LDAP attribute that holds user ID: 282 {ldap_uids, [{"mail", "%u@mydomain.net"}]}. 283 %% 284 %% LDAP filter: 285 {ldap_filter, "(objectClass=inetOrgPerson)"}. 286 Some explanations here: * we deactivate the basic **internal** authentication method by commenting the line. * we activate the ldap method (line 261) * a ldap slave is present locally on the server, so ldap calls are done on localhost:389 without TLS * the ldap search base is **ou=people,dc=mydomain,dc=net**, where users are located in the LDAP tree * JIDs are identical to user's email addresses, so we look for the **mail** attribute to find usernames. This is done with the directive **{ldap_uids, [{"mail", "%u@mydomain.net"}]}.** * and, finally, we restrict the search filter to **inetOrgPerson** object class. no need to look for other types of objects, we only want to match users (and not printers, for examples). In my configuration, there is no need for a LDAP manager. User's credentials can be used to bind to the ldap server and verify the user password. So I left the LDAP MANAGER directives commented out. With these directives, ejabberd should be able to verify the user credentials against the ldap directory. Simply restart your ejabberd server and try to log in again with, this time, the password stored in LDAP, and you should be all set. This configuration does not require that user are registered against the ejabberd server using ejabberdctl command before they can actually log in. ejabberd will rely on the ldap objects directly. ===== MSN transport ===== Using pymsnt as a gateway with ejabberd, it is possible to contact MSN users through a XMPP account. # aptitude install pymsnt The configuration file is ** /etc/pymsnt.conf.xml**. pymsnt needs a DNS records, such as //msn.mydomain.net// that points to the pymsnt gateway. The sample configuration file connect to ejabberd on localhost:5557 and share a random secret token for basic security: msn msn.mydomain.net 127.0.0.1 5557 totodanslecaniveau en http://jabber.mydomain.net 524288 2048 8010 2 Some explanations from the [[http://delx.net.au/projects/pymsnt/docs/server.html|doc]]: * The 'jid' setting should be the ID you want PyMSNt to take on the network. Example: 'msn.host.com'. * The 'host' setting should be a public DNS or IP address of the server the transport is running on. This is needed for file transfer! * The 'mainServer' setting should be the IP address or DNS of the main Jabber server. Default: '127.0.0.1'. * The 'port' setting is the port that PyMSNt and the Jabber server agree to use to connect between them (more details on this below). Default: '5347'. * The 'secret' setting should match the secret specified for component connections in your main Jabber server. It's a password that only the Jabber server and the transport must know. * The 'website' setting should be a website to refer users to. * There are other options in this file that are documented by comments. It is safe to leave them at the default values. * Ensure that the transport can make outgoing connections on port 443 (HTTPS), 1863, as well as incoming connections on 8010 (for Jabber file transfers). In **/etc/ejabberd/ejabberd.cfg**, we need to configure the MSN transport as follow: %% MSN Transport {5557, ejabberd_service, [ {ip, {127, 0, 0, 1}}, {access, all}, {shaper_rule, fast}, {host, "msn.mydomain.net", [{password, "totodanslecaniveau"}]} ]}, **Open TCP/8010**\\ PyMSNt will listens on port TCP/8010 by default for incoming connections. Make sure you open that one in your firewall ;) Restart both daemons, on your local xmpp client (such as PSI), your should see "msn.mydomain.net" in the service discovery section. If you run Lenny. There is a bug between twistd and pymsnt If you have an error message on the **removePID** function, apply the following[[http://py-transports.googlegroups.com/web/pymsnt-1.patch|path]] to main.py as follow: # cd /usr/share/pymsnt/src # patch -p1 < pymsn.patch can t find file to patch at input line 4 Perhaps you used the wrong -p or --strip option? The text leading up to this was: -------------------------- |diff -a -u -d -r pymsnt.a/src/main.py pymsnt.b/src/main.py |--- pymsnt.a/src/main.py 2008-04-04 17:45:43.000000000 +0400 |+++ pymsnt.b/src/main.py 2008-08-05 17:44:17.000000000 +0400 -------------------------- File to patch: main.py patching file main.py # /etc/init.d/pymsnt restart Stopping MSN transport for Jabber: No python found running; none killed. Starting MSN transport for Jabber: Removing stale pidfile /var/run/pymsnt/pymsnt.pid pymsnt. ===== Configure File Transfert ===== ejabberd can act as a proxy between two clients to allow file transfert between those clients. There is a module called **mod_proxy65** for this purpose. The documentation is available here, a basic configuration works as follow, in **ejabberd.cfg**: {mod_proxy65, [ {name, "File transfert service"}, {host, "jabber.mydomain.net"}, {hostname, "jabber.mydomain.net"}, {ip, {0, 0, 0, 0}}, {port, 5270}, {access, local}, {shaper, c2s_shaper} ]}, With: * {host, HostName}: This option defines the Jabber ID of the service. If the host option is not specified, the Jabber ID will be the hostname of the virtual host with the prefix ‘proxy.’. The keyword "@HOST@" is replaced at start time with the real virtual host name. * {name, Text}: Defines Service Discovery name of the service. Default is "SOCKS5 Bytestreams". * {ip, IPTuple}: This option specifies which network interface to listen for. Default is an IP address of the service’s DNS name, or, if fails, {127,0,0,1}. * {port, Number}: This option defines port to listen for incoming connections. Default is 7777. * {hostname, HostName}: Defines a hostname advertised by the service when establishing a session with clients. This is useful when you run the service behind a NAT. The default is the value of ip option. Examples: "proxy.mydomain.org", "200.150.100.50". Note that not all clients understand domain names in stream negotiation, so you should think twice before setting domain name in this option. FIXME -> USE SPECTRUM ! http://spectrum.im/projects/spectrum/wiki