Jappix, Prosody and Nginx
This article is part of an ongoing series of posts, there’s an index here.
Installing Jappix on our little baby XMPP server is a very quick win in a lot of ways. In one hit we get a web-based front end to registration so we can eventually disable/redirect In-Band Registration if we want, we get a basic web presence we can refer people to, and we get a known-working XMPP client to refer end-users with difficulties to for sanity checking.
There are downsides to Jappix as well; I personally don’t really like the look of Jappix out of the box and it’s very limited in options for customisation on that front. It includes features I have no real interest in, and no ability to disable them. As a starting point though, it’s great and will give this project a huge leg-up.
As I think I mentioned early on in this project, I’m also using it to learn my way around Nginx which I’ve never deployed on anything before. This has meant a lot of the time I’m Googling basic things as I go. I’m not going to dwell too much on things like this, but I will point out sources I’m using.
One thing that helped me a lot was this post on setting up Jappix with Nginx (exactly what I needed) and another post from the same site on setting up PHP-FHM. The site is in French, but configuration files are generally the same in any language and Google Translate works really well these days. One thing to note though, if you’re doing the same thing as me, is that Google Translate messes up some whitespace in the config files, so do your copy and pasting from the original, not the translated copy.
Here’s the /etc/nginx/sites-available/xmpp.chinwag.im file I ended up with. I’m using that hostname for now, and I’m reusing the certificate I obtained earlier for Prosody to keep everything encrypted at all times. Eventually I’ll want to move all services to the main root domain name, but let’s take things a step at a time. I’m not going to make Jappix a primary interface in the long term, it’s just going up to get us started quickly.
server {
listen 80;
server_name xmpp.chinwag.im;
return 301 https://xmpp.chinwag.im$request_uri;
}
server {
listen 443 ssl;
server_name xmpp.chinwag.im;
root /var/www/jappix;
index index.html index.php;
access_log /var/log/nginx/xmpp.chinwag.im-access.log;
error_log /var/log/nginx/xmpp.chinwag.im-error.log;
add_header Strict-Transport-Security "max-age = 315360000; includeSubdomains";
ssl_certificate /etc/prosody/certs/chinwag.im.crt;
ssl_certificate_key /etc/prosody/certs/chinwag.im.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_pass php5-fpm-sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n
post_max_size = 100M \n
max_execution_time = 600";
}
location ~ /(app|i18n|log|test|tmp|tools)/ {
deny all;
}
}
Like I’ve said, this is baby’s first Nginx deployment here and I’m copying examples from elsewhere, so constructive suggestions are more than welcomed. I might just move all my configuration examples to my GitHub accountthis week, and link there so I can just take PRs.
BOSH and Websockets
We need to go a little bit further with the Prosody configuration before we can just get things rolling. In a previous post I added mod_bosh and mod_websocket into the configuration, but didn’t do more than that. By default these services which we need to bridge XMPP to a web service will be available on URLs like https://xmpp.chinwag.im:5281/http-bind or wss://xmpp.chinwag.im:5281/xmpp-websocket – which is fine in some respects, but the security model for applications like this generally prevents you connecting to a host other than the one the scripts have been loaded from.
There are a couple of ways to deal with this, and I’m going to set up both to cover current use and the future. First we can set Prosody to allow “cross-domain” connections and we can also use the web server that’s serving the application (Jappix in this case) to proxy the connection to Prosody as well. The proxy is also especially helpful for users who might be behind restrictive firewalls. If we’re operating on the exact same web server instance, we’ll know they can connect since the web site that’s driving it was reachable in the first place.
I’ve added to prosody.cfg.lua:
consider_bosh_secure = true
consider_websocket_secure = true;
cross_domain_bosh = true;
cross_domain_websocket = true;
Note the directives to “consider_(bosh|websocket)_secure” as well as the cross-domain directives. This tells Prosody to assume that even though it’s coming over unencrypted HTTP, to treat it as encrypted. It will be, since we’ll be terminating SSL with Nginx and that’ll be making the actual connection over the local interface. I’ve also used the Prosody configuration to only bind HTTP to the 127.0.0.1 interface, and just to be safe I’ve used the system firewall configuration to block port 5280 from the outside. We could just disable HTTP and proxy to the HTTPS service on 5281, but I don’t see a need to double up on the encryption when there’s no traffic leaving the host and I could just save some CPU time instead.
According to both the Prosody documentation and Jappix as well, an Nginx BOSH proxy configuration looks like this:
location /http-bind {
proxy_pass http://localhost:5280/http-bind;
proxy_set_header Host $host;
proxy_buffering off;
tcp_nodelay on;
}
I’ve been reading up on proxying websockets, but I’m not feeling quite up to speed on it yet so I’m not going to include a config file example here now in case I’m not quite right about something. My setup does work, but it seems a little glitchy in some circumstances and I don’t want to go leading any future search result followers astray. If you’re visiting from the future, feel free to give me a yell if I haven’t posted a followup on this yet. Also, if you’re currently running a setup the same as this and you’ve got it all sorted out, please share your work.
Installing Jappix
My French web site from earlier did a really good job of covering the actual Jappix install, which is really easy from this point on. The only thing I don’t really like is the part where they’re making the entire Jappix installation writable by the web server process. If you do this, you get a nice option to upgrade the service from inside its own management interface, but with a complex application like this, the high possibility of compromise really worries me. I’d rather schedule some downtime, unpack a fresh copy, move my configuration over and re-test. It’s your call. I recommend never letting a web application have the ability to overwrite its own core files, call me crazy if you will.
The other thing I found worth noting is that the setup process only prompts you for the BOSH host, the websocket URL field isn’t visible until after you’ve done the installation and you go into management. In my tests so far, websocket access to Jappix has been amazingly more reliable and stable than BOSH, so don’t skip sorting this out – your users will have a much better time as a result. There will likely be some firewall restrictions in place from time to time, or browser compatibility issues that’ll make maintaining BOSH a necessary fallback option though.
This brings us pretty much to the point where I can link to https://xmpp.chinwag.im/ and say “look, here’s a full, usable XMPP service”. You can register, sign in, join chat rooms and even do some voice and audio chat things where client support exists. I’ll get into that fairly soon.
Next up, I’ll make a real front-end web site, and we’ll start advertising the service.