XMPP Server - Prosody
-
I had AI look at this, but it wasn't very fruitful. It suggests using Cloudron's wildcards and, for Prosody, using its ACME plugin which can obtain certificates:"Let's dive into the specific technical challenges and potential solutions for using Prosody on Cloudron:
- Domain Flexibility with Prosody:
Prosody is indeed more flexible than Snikket in this regard. You can configure Prosody to use different domains for XMPP addresses and the web portal. The key configuration options you'd need to focus on are:
VirtualHost "example.com" -- XMPP domain configuration http_host = "chat.example.com" -- Web interface domain
This separation isn't possible with Snikket's current setup.
- Certificate Handling:
The core issue here is obtaining a certificate for example.com when your Cloudron app is hosted at chat.example.com. Prosody itself doesn't solve this, but there are potential workarounds:
a) Use Cloudron's wildcard certificate: If Cloudron provides a wildcard cert for *.example.com, you could use this for both the chat subdomain and the main domain.
b) Manual certificate injection: You could manually obtain a certificate for example.com and inject it into the Prosody container. This would require modifying the Cloudron app to allow for custom certificate paths:
ssl = { certificate = "/path/to/custom/fullchain.pem"; key = "/path/to/custom/privkey.pem"; }
c) ACME plugin: Prosody has an ACME plugin that can obtain certificates. You'd need to ensure the Cloudron app has the necessary permissions to write these certificates and that the DNS challenge is used instead of HTTP:
plugin_paths = { "/usr/lib/prosody/modules" } modules_enabled = { "acme" } acme = { provider = "letsencrypt", challenge = "dns-01" }
- SRV Records:
Cloudron should allow you to set custom DNS records. You'd need to add:
_xmpp-client._tcp.example.com. IN SRV 0 5 5222 chat.example.com. _xmpp-server._tcp.example.com. IN SRV 0 5 5269 chat.example.com.
- Cloudron Integration:
The existing GitHub repo is a start, but you'd need to modify it to:
- Allow for custom domain configuration
- Implement one of the certificate solutions mentioned above
- Possibly add a script to automatically update SRV records via Cloudron's API
- Reducing Overhead:
Unlike Snikket, Prosody doesn't include a TURN/STUN server by default. You could either:
a) Use an external TURN/STUN service
b) Implement a lightweight TURN/STUN server as a separate Cloudron app
To move forward:
- Fork the existing Cloudron-Prosody repo and start implementing these changes.
- Engage with Cloudron's development team about adding an option for apps to use the primary domain certificate. This would solve many of these issues at the platform level.
- Consider implementing a custom module for Prosody that interfaces with Cloudron's API for dynamic configuration and certificate management.
This approach should address the specific technical challenges you're facing with implementing an XMPP server on Cloudron while taking advantage of Prosody's flexibility. The key will be integrating Prosody's configurability with Cloudron's app structure and API."
- Domain Flexibility with Prosody:
-
@girish - Given that the only blocker (that I know of so far) is the TLD certificate availability, would it be a decent workaround to package this entire thing as an application, and then run a one-time command to symlink the TLD certificate into the application directory? This would allow the sys admin to choose which application deserves access to the TLD certificate, ensure the app always has access to the latest certificate, and work around the current limitation of apps not being able to request the TLD cert.
-
@djxx Just re-reading this thread again. From what I gathered, the app can be installed in app.domain.com but can use handles like @domain.com . For that matter, it can use handles like @handle.domain.com as well. All a matter of configuration . For this, the app requires certs of whatever the handle domain is.
I guess we need some sort of option field in the 'tls' addon - https://docs.cloudron.io/packaging/addons/#tls . But I am unable to quickly think of a way how to specify this in the manifest
-
Is https://github.com/DerekJarvis/cloudron-prosody still the latest code ? If I build that app, does it work?
-
Small update - made good progress. What's working so far:
- XMPP server starts up, messages can be sent/received
- this includes the pretty user@domain.com username
- LDAP Auth works, so users use their own accounts
- App health check
What doesn't work: anything related to HTTP endpoints (e.g. file uploads) . I've opened a ticket here: https://forum.cloudron.io/topic/13539/custom-app-httpport-not-being-proxied
- XMPP server starts up, messages can be sent/received
-
Big update - I'm ready for your help @girish .
Repo: https://github.com/DerekJarvis/cloudron-prosody
Docker Image: derekjarvis/cloudron-prosody:20250406-042328-86345387dWhat's working:
Everything.
This is a full-fledged XMPP server with:
- Healthcheck
- TURN server for jingle calling (voice and video)
- Uses LDAP for auth and automatically works for all users
- Supports sending/receiving files and images
- Supports message carbons (sync messages between devices)
- Supports standard (pretty) accounts like user@domain.com
(Direct TLS failing is intentional because it's deprecated and our upstream doesn't bother supporting it)Approach:
I forked from a repository that has a good Prosody docker image with VERY secure defaults. I actually had to loosen a couple to make it more user friendly (like not requiring OMEMO). I added on the modules that make it even more useful (like HTTP upload for file attachments, TURN for jingle calls). I tried to make the majority of changes in our own files to make it easier to compare to upstream. I used Cloudron services (like LDAP, TURN) instead of running extra services in the container.
Help Needed:
There are a few VERY hacky things I had to do to make this work. Only one is a total blocker for real deployment by others, the others are all "nice to have" improvements.
TLD (Top-Level Domain) certs (blocker)
XMPP needs the TLD cert to allow the standard (and pretty) usernames like
user@domain.com
. Right now, there's no way for a Cloudron app to request the TLD cert. To work around this I did the following:- Made a storage volume for the app
- Copied all the certs into the storage volume (yes, this is a very bad idea)
cp -f /home/yellowtent/platformdata/nginx/cert/* /mnt/HC_Volume_102134826/app_xmpp/
- Have the app use the certs it needs and then delete everything else
Aliases needed (nice to have)
The app is installed under the "xmpp" domain, and 4 aliases must be defined in the app, and also in the DNS records:
It would be great if the Manifest allowed us to specify additional aliases the app needs, and also added the DNS records for those if the Cloudron instance is using a DNS API
SRV Records (nice to have)
XMPP uses SRV records to give information about the XMPP servers for a domain. Specifically:
- _xmpp-client._tcp.domain.tld port 5222
- _xmpps-client._tcp.domain.tld port 5223
- _xmpp-server._tcp.domain.tld port 5269
Add these manually, and your app will work fine.
It would be ideal if Cloudron allowed specifying additional DNS records that should be created if the Cloudron instance is using a DNS API.
Admin users (nice to have)
I couldn't find a way to identify Cloudron Admin users through LDAP. Prosody supports specifying an LDAP query to indicate who is an admin of the XMPP server.
It would be ideal if there was an LDAP query that could identify Admin users, and then Prosody would mark those users as admins as well.
External IP for checks (nice to have)
Prosody has a very convenient
check
function which will check for common errors. One of the things it checks for is if it thinks it is reachable by checking the DNS records it uses against the IP addresses it knows it has. So in the Prosody config you can tell it the known IP addresses for the server, and thecheck
function will compare that to the DNS records to confirm the server is reachable.It would be ideal if we have an environment variable for the server's primary IP address. With this the checks would not show so many false positives.
TLD environment variable (nice to have)
Prosody needs to know the TLD it is serving. I did not find an environment variable for this, so I hacked one using bash:
export DOMAIN=${CLOUDRON_WEBADMIN_ORIGIN#https://my.}
It would be ideal if Cloudron exposed an environment variable for the TLD.
Other Comments
The #1 reason I use XMPP is for self-hosted private chat. The #2 reason is it's how I have a virtual phone number for SMS. I use https://jmp.chat to get a VERY CHEAP sms number and use XMPP on all my devices to communicate with people through SMS on convenient chat apps like Gajim and Conversations.
-
Big update - I'm ready for your help @girish .
Repo: https://github.com/DerekJarvis/cloudron-prosody
Docker Image: derekjarvis/cloudron-prosody:20250406-042328-86345387dWhat's working:
Everything.
This is a full-fledged XMPP server with:
- Healthcheck
- TURN server for jingle calling (voice and video)
- Uses LDAP for auth and automatically works for all users
- Supports sending/receiving files and images
- Supports message carbons (sync messages between devices)
- Supports standard (pretty) accounts like user@domain.com
(Direct TLS failing is intentional because it's deprecated and our upstream doesn't bother supporting it)Approach:
I forked from a repository that has a good Prosody docker image with VERY secure defaults. I actually had to loosen a couple to make it more user friendly (like not requiring OMEMO). I added on the modules that make it even more useful (like HTTP upload for file attachments, TURN for jingle calls). I tried to make the majority of changes in our own files to make it easier to compare to upstream. I used Cloudron services (like LDAP, TURN) instead of running extra services in the container.
Help Needed:
There are a few VERY hacky things I had to do to make this work. Only one is a total blocker for real deployment by others, the others are all "nice to have" improvements.
TLD (Top-Level Domain) certs (blocker)
XMPP needs the TLD cert to allow the standard (and pretty) usernames like
user@domain.com
. Right now, there's no way for a Cloudron app to request the TLD cert. To work around this I did the following:- Made a storage volume for the app
- Copied all the certs into the storage volume (yes, this is a very bad idea)
cp -f /home/yellowtent/platformdata/nginx/cert/* /mnt/HC_Volume_102134826/app_xmpp/
- Have the app use the certs it needs and then delete everything else
Aliases needed (nice to have)
The app is installed under the "xmpp" domain, and 4 aliases must be defined in the app, and also in the DNS records:
It would be great if the Manifest allowed us to specify additional aliases the app needs, and also added the DNS records for those if the Cloudron instance is using a DNS API
SRV Records (nice to have)
XMPP uses SRV records to give information about the XMPP servers for a domain. Specifically:
- _xmpp-client._tcp.domain.tld port 5222
- _xmpps-client._tcp.domain.tld port 5223
- _xmpp-server._tcp.domain.tld port 5269
Add these manually, and your app will work fine.
It would be ideal if Cloudron allowed specifying additional DNS records that should be created if the Cloudron instance is using a DNS API.
Admin users (nice to have)
I couldn't find a way to identify Cloudron Admin users through LDAP. Prosody supports specifying an LDAP query to indicate who is an admin of the XMPP server.
It would be ideal if there was an LDAP query that could identify Admin users, and then Prosody would mark those users as admins as well.
External IP for checks (nice to have)
Prosody has a very convenient
check
function which will check for common errors. One of the things it checks for is if it thinks it is reachable by checking the DNS records it uses against the IP addresses it knows it has. So in the Prosody config you can tell it the known IP addresses for the server, and thecheck
function will compare that to the DNS records to confirm the server is reachable.It would be ideal if we have an environment variable for the server's primary IP address. With this the checks would not show so many false positives.
TLD environment variable (nice to have)
Prosody needs to know the TLD it is serving. I did not find an environment variable for this, so I hacked one using bash:
export DOMAIN=${CLOUDRON_WEBADMIN_ORIGIN#https://my.}
It would be ideal if Cloudron exposed an environment variable for the TLD.
Other Comments
The #1 reason I use XMPP is for self-hosted private chat. The #2 reason is it's how I have a virtual phone number for SMS. I use https://jmp.chat to get a VERY CHEAP sms number and use XMPP on all my devices to communicate with people through SMS on convenient chat apps like Gajim and Conversations.
-
@djxx very nice! I just tried to quickly test this and building from your repo, the app fails to start with the following error:
cp: cannot stat '/media/Extra Storage/app_xmpp/*': No such file or directory
probably some oversight there, but don't have time to dig into this just now.
-
@djxx very nice! I just tried to quickly test this and building from your repo, the app fails to start with the following error:
cp: cannot stat '/media/Extra Storage/app_xmpp/*': No such file or directory
probably some oversight there, but don't have time to dig into this just now.
@nebulon - Correct. You would need to make a volume called "Extra Storage", mount it onto the app, and have a folder under it called app_xmpp where you copy the sub-domain and TLD certs you need (I just copied all of them). This is an ugly workaround for the TLD cert not being available to the app through the manifest configuration.
You would also need to add a few alias domains and DNS records manually.
-
I guess some clear set up instructions would help
- install the app to the 'xmpp' subdomain
- add extra DNS CNAME/A records pointing to the same server:
- pubsub
- proxy
- upload
- conference
- add these domains as aliases for the app
- add DNS SRV records:
- _xmpp-client._tcp.domain.tld port 5222
- _xmpps-client._tcp.domain.tld port 5223
- _xmpp-server._tcp.domain.tld port 5269
- make a storage volume called "Extra Storage"
- mount the storage volume to the app
- copy certs into the storage under the subdirectory app_xmpp
- for my server, the command looks like this (run in cloudron directly, not in the app terminal):
cp -f /home/yellowtent/platformdata/nginx/cert/* /mnt/HC_Volume_102134826/app_xmpp/
- restart the app to make sure it's starting up with all pieces in place (extra domains, DNS records, and certs)
Then you can check for compliance with these two tools:
-
I took the time to take a bit closer into the package. There is a long way to go to make it compliant with Cloudron packaging. It is a great start and we don't provide good information on what is expected from Cloudron packages, so this is mostly our fault.
For a start, you already added the
tls
addon, which does provide the certs in/etc/certs/
using that instead of the extra volume mount will ensure the app gets restarted and will pick up the correct certs if they get renewed. I am playing a bit from your package to hopefully get this working.One question is, apparently certs for the root domain are also required, even though no DNS records for that are? This can be solved by also adding the root domain as an alias to the app.
For the extra required DNS records, we have to see if and how we can integrate this in the platform to support those. We already have some well-known type records, maybe it fits there.
Otherwise there are naturally discrepancies elsewhere, given that this started (as far as I can tell) from some upstream Dockerimage which will not fit, but we can get through one by one. The first I noticed was, that currently it doesn't work with the Cloudron app debug mode, since it uses
ENTRYPOINT
in Dockerfile, that was easy to change by relying only onCMD
-
I've pushed my changes as a PR https://github.com/DerekJarvis/cloudron-prosody/pull/1
No need to merge that, just to give some idea about the certs handling. The alias domains still need to be manually setup by the user, so maybe we need some platform feature in the future for this.
-
I've pushed my changes as a PR https://github.com/DerekJarvis/cloudron-prosody/pull/1
No need to merge that, just to give some idea about the certs handling. The alias domains still need to be manually setup by the user, so maybe we need some platform feature in the future for this.
@nebulon said in XMPP Server - Prosody:
The alias domains still need to be manually setup by the user, so maybe we need some platform feature in the future for this.
Don't we already have this for e.g. apps like Loomio?
-
loomio and others like minio have multiple http ports (which are routed through the reverse proxy). If I understand the xmpp use-case correctly, then those are just alias endpoints not serving any http content. Even the current main domain will just produce a 404 when hit with a browser. The alias use is more like a bit of a workaround to get DNS setup as well as get valid certs.
-
I took the time to take a bit closer into the package. There is a long way to go to make it compliant with Cloudron packaging. It is a great start and we don't provide good information on what is expected from Cloudron packages, so this is mostly our fault.
For a start, you already added the
tls
addon, which does provide the certs in/etc/certs/
using that instead of the extra volume mount will ensure the app gets restarted and will pick up the correct certs if they get renewed. I am playing a bit from your package to hopefully get this working.One question is, apparently certs for the root domain are also required, even though no DNS records for that are? This can be solved by also adding the root domain as an alias to the app.
For the extra required DNS records, we have to see if and how we can integrate this in the platform to support those. We already have some well-known type records, maybe it fits there.
Otherwise there are naturally discrepancies elsewhere, given that this started (as far as I can tell) from some upstream Dockerimage which will not fit, but we can get through one by one. The first I noticed was, that currently it doesn't work with the Cloudron app debug mode, since it uses
ENTRYPOINT
in Dockerfile, that was easy to change by relying only onCMD
@nebulon Thanks for taking a look through. I am certainly open to making changes to make it a compliant package - I just don't know what those changes are.
I didn't realize that the TLD could be added as an alias which would give the cert. I was using /etc/certs before I did the hacky workaround to get the TLD. I will try the approach you mentioned and make sure things are still working.
One question is, apparently certs for the root domain are also required, even though no DNS records for that are?
Correct. The XMPP specification uses the SRV records to point to a specific server. It does need the cert to validate usernames like
user@domain.com
even if you chose to have the XMPP server somewhere else completely.Otherwise there are naturally discrepancies elsewhere, given that this started (as far as I can tell) from some upstream Dockerimage which will not fit, but we can get through one by one.
Can you tell me a bit more about this? I changed it to use the Cloudron base, and confirmed that all the actions it does to build the server are successful. I did my best to follow putting data under /app/data, and runtime files under /run, according to the documentation.
For the extra required DNS records, we have to see if and how we can integrate this in the platform to support those. We already have some well-known type records, maybe it fits there.
This would be nice, but it's really not that difficult (compared to the entirety of setting up an XMPP server). I think if the package was installable and the only extra steps are some DNS changes, it'd still be very easy for people who want XMPP to do.
-
Yes, we have to add this to the platform as a feature as well as think about how to make the DNS setup for those extra domains required. We have a policy that apps should "work" after installation. Do you know if there is a way that maybe those extra (sub)domains and thus their certs can be optional, just limiting the functionality of the app?