DA 1.712: vhost of domain A uses the SSL certificate file of domain B (same user), serving an expired cert while A's own ACME cert is valid

castris

Verified User
Joined
Apr 16, 2021
Messages
171
Location
Arcenillas
Hi,

DirectAdmin 1.712 (183b67368b137bca2765445f19ece9b2dfc53132), nginx_apache, Let's Encrypt via lego.

Setup
One user, two domains:
  • example.com — acme_enabled=yes, renews fine. Its own example.com.cert.combined holds a valid wildcard (*.example.com + example.com).
  • example.es — acme_enabled was NOT set. Its example.es.cert.combined held an old, manually installed certificate whose SAN was *.example.com + example.com (not .es). That certificate expired months ago.

What happened
After a successful ACME renewal of example.com (files rewritten, user's nginx.conf and httpd.conf regenerated in the same minute), the HTTPS vhost of example.com was still generated with:

Code:
ssl_certificate /usr/local/directadmin/data/users/USER/domains/example.es.cert.combined;
SSLCertificateFile /usr/local/directadmin/data/users/USER/domains/example.es.cert.combined

So example.com served the expired certificate stored under example.es (browser: NET::ERR_CERT_DATE_INVALID), even though example.com.cert.combined was valid. The vhost of example.es pointed to the same file.

No custom HTTPD/nginx config for either domain, no pointers, no custom templates involved for this user.

How it was resolved
Requesting an ACME certificate for example.es (letsencrypt.sh request example.es) replaced example.es.cert.combined with a real *.example.es certificate. On that rewrite, DA regenerated the vhost of example.com pointing to example.com.cert.combined. Both domains are fine now.

Questions
  1. How does DA choose which certificate file goes into a domain's vhost? It looks like it picked another domain's certificate because its SAN covered the hostname.
  2. Should a domain's own valid certificate always win over another domain's certificate that merely covers the same name?
  3. Should an expired certificate ever be selected when a valid one exists for that hostname?

Hosting users install certificates manually on the "wrong" domain more often than one would hope, and today this produces a silent outage that survives every successful renewal. Is this expected behaviour, and is there a setting to make each domain always use its own certificate?

Thanks.
 
How does DA choose which certificate file goes into a domain's vhost? It looks like it picked another domain's certificate because its SAN covered the hostname.
Any certificate owned by the user could be used in web server configuration. This means user can have "cross-domain" certificates but he can only affect the domains he own. Certificates are never shared between users.

Exact matching certificates are preferred, this means if user has cert A for www.example.com and cert B for *.example.com, the cert A will be preferred for the www.example.com host name.

If user has two certificates that cover the same names the order is undefined (usually the last one wins).

Should a domain's own valid certificate always win over another domain's certificate that merely covers the same name?
Current implementation does not take into consideration what domain certificate is associated with, only association with the user name is used. So all certificate files owned by a single user are considered equal.

If this really causes problems we could improve the certificate mapping logic. Our initial expectation was that it should not be too hard for the user to check his manually uploaded certificates and resolve problems if there are any.

In the long term I think manually managed certificates will be used very rarely. It will require a lot of effort maintaining them once the maximum certificate lifetime is reduced to 47 days. So even paid certificates will use ACME for auto-renewal.

Should an expired certificate ever be selected when a valid one exists for that hostname?
Yes. This allows the certificate selection to stay stable when certificates expire and makes the certificate selection logic simpler.
 
Thanks for the clear explanation, that answers all three questions.

Points 1 and 2 make sense to me: user-scoped selection, exact match before wildcard, and certs never crossing users. I can live with that and handle it on my side.

Point 3 is the one I would like to push back on, because it is what turned a harmless overlap into an outage:

  • The user had a valid ACME certificate for example.com.
  • The vhost of example.com served the expired certificate of example.es, because both covered the same names.
  • Visitors got a certificate error on a domain whose own certificate was perfectly fine.

I understand the stability argument, but from the visitor's point of view an expired certificate is never a usable choice. Picking it means the selection is stable in the worst way: it stays broken until someone notices.

Would you consider one small rule, without changing the rest of the logic?

When two or more certificates of the same user cover the same names, never select an expired one if a valid one exists.

That keeps the current ordering for every valid candidate, so it should not affect stability in normal operation. It only changes the result in the case where the current behaviour serves a broken certificate.

If that is not possible, a warning in the panel or in the logs when a vhost ends up with an expired certificate while a valid one is available would already help a lot. Today it fails silently, and we only found it because a client complained.

Thanks again.
 
It is hard to enforce the rule that snidomains should never contain expired certificates. Because the certificate can be valid at the time when snidomains file is being generated, but it can become expired after some time. And there is no mechanism that would trigger the snidomains file rewrite when it happens.

However, the workflow you have described (how a user can accidentally mess up his configuration) can happen, and we need to make it harder for users to mess it up. Our plan is to make the following changes:
  • When a user is manually uploading a certificate, reject certificates if they do not cover any DNS names for the domain this certificate is being uploaded to. This will prevent users from uploading certs to the wrong domain.
  • When generating the snidomains list and we have multiple candidates for the same DNS name, prefer the DNS name that matches the domain this certificate belongs to.
Either one of these should be enough to protect users from tripping on this problem again.

But we will continue using expired certificates if the user has not updated them in time. Avoiding expired certificates would make the problems much much harder to debug. Instead of getting the error that your certificate is expired, users will start getting the server hostname certificate. And server hostname certificate will not cover user domain, so TLS will break because the incorrect certificate is being used instead of using the correct but expired certificate.
 
Back
Top