Ejabberd: s2s fails due to "unsupported certificate purpose"

Hi there,

I’ve been able to deploy a functional certificate for apache and can also use it for dovecot (IMAP) and postfix (SMTP). So far I’m very happy with the ease of use, thanks Let’s Encrypt!

Now I’m trying to finally replace the self-signed certificate I’ve been using for ejabberd (XMPP) in order to open up server-to-server connections (s2s). I’ve followed the advice to cat my private key, the fullchain.pem as well as the CA PEM from the LE homepage into a new /etc/ejabberd/ejabberd.pem. Everything looks good for normal client-2-server connections and I’m able to resume normal service within my walled garden, but trying to send a message to echo(at)jabber.org fails due to the following message in my logfile (domainname changed to domain.tld for obvious reasons):

2016-01-02 13:05:21.404 [info] <0.539.0>(at)ejabberd_s2s:new_connection:456 New s2s connection started <0.551.0>
2016-01-02 13:05:21.404 [info] <0.551.0>(at)ejabberd_s2s_out:log_s2s_out:1315 Trying to open s2s connection: domain.tld -> jabber.org with TLS=true
2016-01-02 13:05:22.543 [info] <0.533.0>(at)ejabberd_listener:accept:313 (#Port<0.6872>) Accepted connection ::FFFF: -> ::FFFF:
2016-01-02 13:05:23.011 [info] <0.555.0>(at)ejabberd_s2s_in:wait_for_stream:251 Closing s2s connection: domain.tld <–> jabber.org (unsupported certificate purpose)
2016-01-02 13:10:22.238 [info] <0.551.0>(at)ejabberd_s2s_out:wait_for_validation:486 wait for validation: domain.tld -> jabber.org (xmlstreamend)

The problem seems to be that my XMPP server sort of acts as a client when connecting to another server, which seems to contradict the intended purposes of the LE certificate. Any idea on how to circumvent this?



You might have already noticed what the problem is, but just to document it for others running into the same problem:

XMPP s2s connections not only use the certificate as server certificate, but also as client certificate.
Let’s consider an example with 2 servers: S1 owning certificate C1 and S2 owning certificate C2.
If S1 connects to S2, S2 presents C2 as server certificate and S1 presents C1 as client certificate.
The same also applies in the other direction.
If S2 connects to S1, S1 presents C1 as server certificate and S2 presents C2 as client certificate.
The “certificate direction” is only determined by the TCP connection. TCP clients provide client certificates, TCP servers provide server certificates.

To use a certificate as server certificate it is necessary to have the Extended Key Usage “TLS Web Server Authentication”.
As client certificate it is necessary to have the Extended Key Usage “TLS Web Client Authentication”.
Let’s Encrypt certificates already allow both:
X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication

In your case jabber.org connected to your server (incoming s2s connection).
Therefore, your Let’s Encrypt certificate was presented as server certificate and jabber.org presented its certificate as client certificate. The problem here is that a lot of XMPP servers are using certificates, which do not have “TLS Web Client Authentication” set. That is exactly, why you get the “unsupported certificate purpose” error during certificate verification. Last time I checked it, also StartCom’s XMPP certificates didn’t have the “TLS Web Client Authentication” Extended Key Usage set.
jabber.org already fixed the issue on their end, so it should work right now. My guess is they switch from StartCom’s XMPP certificate to their usual webserver certificate, but I am not sure about that.

Sadly, XMPP Observatory (https://www.xmpp.net/) doesn’t check for that issue. I hope this will change in the future.

I am only aware of two options you have with ejabberd right now:

  1. accept the fact that you can not connect to some servers or
  2. disable certificate verification completely by setting s2s_use_starttls to required instead of required_trusted
    I highly discourage to use the second option. I would go for the first option and try to contact administrators to fix their faulty servers.

Please all use fully valid certificates with your XMPP servers!

As a sidenote: I am successfully using a Let’s Encrypt certificate on my XMPP server. Thank you for that!
You only have to be careful to include all necessary alternative names (e.g. conference.domain.tld and so on).

1 Like

[quote=“nZyMe, post:1, topic:8113”]
(domainname changed to domain.tld for obvious reasons):
dig -x
made that obsolete ?

I apologize for necroposting, but this seems to be the most relevant thread I have found.

To clarify what is above: Is there a RECOMMENDED “canonical” acme/certbot invocation that should be used when requesting a certificate to be used for a jabber server (specifically ejabberd in this case)?

As part of this question, if I’m already using a LE certificate for my web server, should I request a separate certificate for my XMPP server, or just extend my existing certificate with additional subdomains? I’m assuming the latter would be the preferred solution, but certbot complains that it cannot find an Apache VHOST matching the subdomain of my jabber server.

Of course, I suppose I could just change the certificate to be for the entire domain…?

Not that I’m aware of - there’s not a whole lot to customize on LE certificates besides key size and domains.

First, Let’s Encrypt doesn’t support wildcards (yet), so you won’t be able to get one for your entire domain until January. The rest is an implementation question. Based on the fact that you’re using the Apache plugin, I think the ‘cleanest’ method is to add a stub vhost to your Apache configs so that authorization is taken care of the same way as it is for your primary domain. Perhaps just serve a 404 or a 301 to your primary domain on HTTP(S) for this stub normally. Then just add a post hook (renewal hook) to the certbot command to reload the certificate on the ejabberd server (the Apache plugin does this for Apache automatically) and you’ll be good to go.

Won’t a 404 result in a failed verification? I’ve been trying the stub-vhost method, but the stub vhost fails tls-sni-01 verification because the jabber.mydomain vhost returns the tls-sni-01 challenge certificate for www.mydomain.

Apache uses tls-sni-01, not http-01, so it doesn’t matter what you serve there. I was assuming you’d direct just 443 for the Jabber domain to the vhost through your firewall, and everything else (I’m assuming XMPP uses different ports - at least I’m not aware of it using 443) to the actual server.

I tried just 80, just 443, and 80 and 443. As it happens, jabber and apache are currently on the same host anyway, but any of them could potentially get moved to different servers, which is why they have their own hostnames. The problem I’m running into seems to be that if jabber.mydomain isn’t served by apache, then verifying jabber.mydomain fails because the verifier can’t connect, and if it is, then verification fails because the verifier gets the same tls-sni-01 cert back from jabber that it does from www. And I don’t know enough about tls-sni-01 to know what I can do about it.

I suspect the root problem here is that LE seems at the moment to be very webserver-centric and really not designed to accommodate creating certificates for other services that it (maybe) needs to verify differently…? XMPP uses a crapton of ports, but 80 and 443 aren’t among them.

I had a similar problem, except the jabber and web servers were on different hosts. I eventually set up a really stupid cron job to generate cert/key pairs on the web server and scp them over to the jabber server, which had another cron job to install them. -.-

(For proper security, I’d actually have the key stay on the jabber server and just keep a CSR on the web server for the cert generation, but my client (“dehydrated”) doesn’t support this, and I ran out of giving a crap.)

Oh yeah, totally. I can't use the raw LE cert anyway with ejabberd because of the way ejabberd handles certificates sometimes as a client and sometimes as a server, so it needs a single cert file — but it's trivial to make that from the LE certificates with a cron job. That was my plan — a cron job that checks the LE certs and makes a new cert for ejabberd from them any time they're less than 24 hours old when it runs.

@netrixx — you said above in this post that you have this working. How did YOU solve the problem of LE verification for the XMPP server?

That’s what the DNS verification method is made for. The main Certbot client isn’t as developed for that type of method, but some clients such as acme.sh support many DNS providers for automating the DNS verification.

Maybe I need to look into whether I can get the cert updated that way then.

If I can get verification managed somehow, will certbot handle the renewals?

Use the client that works for you with that kind of authentication. If you can get Certbot to do it, then go with that. Otherwise, you may need to use a different client.

Like some people already mentioned, it is a good idea to use DNS challenges instead of HTTP.
I am using dehydrated with --challenge dns-01 and an appropriate hook script to publish challenges on my DNS servers.
I am using this setup since I started using Let’s Encrypt for all services that need certificates, as it does not require additional services deployed or ports open. It works equally well for XMPP servers as for HTTP servers or any other service.

Thank you @netrixx ! I will take a look at that.