Tunnelled Server - Error: The client lacks sufficient authorization

I know there is a couple of threats on "The client lacks sufficient authorization" already. I think, however, my problem is a bit different, and I did not want to hijack any of the other threats.

So I have a webserver (apache2) serving a couple of subdomains with a self-signed wildcard certificate. I would like to replace that with letsencrypt certificates. So instead of using a wildcard certificate, I try to create a certficiate for all subdomains:

/usr/local/src/letsencrypt/letsencrypt-auto --apache --debug -d example.com -d www.example.com -d mail.example.com -d calendar.example.com -d mailgate.example.com -d example.dyndns.com

That works - for all but mailgate.example.com, which throws the well-known error:

File "/root/.local/share/letsencrypt/bin/letsencrypt", line 11, in
sys.exit(main())
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 1396, in main
return args.func(args, config, plugins)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 557, in run
lineage = _auth_from_domains(le_client, config, domains)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/cli.py", line 389, in _auth_from_domains
new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/client.py", line 266, in obtain_certificate
return self._obtain_certificate(domains, csr) + (key, csr)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/client.py", line 224, in _obtain_certificate
authzr = self.auth_handler.get_authorizations(domains)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/auth_handler.py", line 84, in get_authorizations
self._respond(cont_resp, dv_resp, best_effort)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/auth_handler.py", line 142, in _respond
self._poll_challenges(chall_update, best_effort)
File "/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/letsencrypt/auth_handler.py", line 204, in _poll_challenges
raise errors.FailedChallenges(all_failed_achalls)
FailedChallenges: Failed authorization procedure. mailgate.example.com (tls-sni-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Correct zName not found for TLS SNI challenge. Found example.com, *.example.com, example2.com, *.example2.com

IMPORTANT NOTES:

  • The following 'urn:acme:error:unauthorized' errors were reported by
    the server:

Domains: mailgate.example.com
Error: The client lacks sufficient authorization

So what's special about mailgate.example.com: That's a different IP address which does not really do anything but route requests through a VPN tunnel to my server (apache, etc.). Replies are routed back to that IP, and from there - with the sender IP modified back into mailgate's public IP - to the client. The reason for this is very simple: I am typically on a dial-up IP with quite some restrictions. The mailgate machine is a virtual host with a "real, fixed IP".

This setup works well for my regular mail traffic, and also for serving ports 80 and 443 on the mailgate IP. But it appears to cause trouble with letsencrypt's verification.

Any hint how I can work around this? If acme needs another port than 80 and 443 to be routed through the tunnel to work properly, I could surely add that.

Thanks!

Could you be a bit more specific about how this setup works? How does this routing work?

The rest of my reply is speculation.

If mailgate.example.com is running on a separate webserver, which then uses a reverse proxy to your "regular" apache routed through your VPN, then letsencrypt won't be able to modify the SSL configuration of that domain (because SSL is terminated on another webserver), which is necessary to complete the tls-sni-01 challenge. Since that web server is the one doing SSL termination, you would have to run the client on that host, and install your mailgate certificate on that web server.

Thanks, it’s actually a bit simpler (I hope):

mailgate uses NAT in its iptables firewall:

$IPTABLES -t nat -A PREROUTING -i $DEV_INTERNET -p tcp --dport 80 -j DNAT --to-destination 10.10.0.1:20080
$IPTABLES -A FORWARD -i $DEV_INTERNET -m state --state NEW -p tcp -d 10.10.0.1 --dport 20080 -j ACCEPT
$IPTABLES -t nat -A PREROUTING -i $DEV_INTERNET -p tcp --dport 443 -j DNAT --to-destination 10.10.0.1:20443
$IPTABLES -A FORWARD -i $DEV_INTERNET -m state --state NEW -p tcp -d 10.10.0.1 --dport 20443 -j ACCEPT

10.10.0.1 is the internal server. Requests on the dial-up IP are fed to it via port forwarding from the router (to 80, 443). In addition, it serves ports 20080 and 20443 from mailgate:

  <VirtualHost *:20080>
    ServerName mailgate.example.com
    DocumentRoot [...]
  </VirtualHost>

  <VirtualHost *:20443>
    ServerName mailgate.example.com
    DocumentRoot [...]
  </VirtualHost>

Responses from ports 20080 and 20443 are then routed back to mailgate, and from there (NAT back) to the respective client, while responses on ports 80 and 443 are routed towards the default gateway and from there to the other clients.

All IP routing, no proxy. Works for http, https, imaps, smtps. It does cause issues for letsencrypt, though. Maybe because on the apache server mailgate is served on different than the standard ports? Any workaround? Maybe I should try --webroot instead of --apache?

Here’s what I think is happening:

  • The client adds temporary vhosts used to solve the tls-sni-01 challenge for each of your domains. By default this vhost runs on port 443.
  • Let’s Encrypt’s CA server connects to your NAT box on 443. This is forwarded to 20443 on your apache server.
  • Because the temporary vhost is listening on port 443 instead, apache doesn’t pick that vhost, but goes for your regular mailgate vhost on port 20443. This vhost doesn’t use the certificate that would be required to verify ownership and solve the challenge.

I believe the client does support a --tls-sni-01-port flag with which you could specify a different port. However, since your config uses both 443 and 20443, this would make all the other domains on port 443 fail. I believe --webroot would indeed be the simplest option, as long as you’re comfortable with doing the SSL configuration on your own.

Not sure I’m comfortable, but I’m running out of ideas for alternatives.

As you appear to have great knowledge about the client: Would it work to get to the certificate in two steps?

  1. Get a certficiate for the subdomains served on 80/443
  2. Expand the certificate by adding subdomains served on 20080/20443

Or does letsencrypt validate all -d domains specified every time it’s called?

Apparently the client supports combining various plugins (how did I not see this before :sweat_smile:), so you can use --webroot with --apache as follows:

/letsencrypt-auto --authenticator webroot --installer apache \
-w /webroot/of/mailgate -d mailgate.example.com \
-w /webroot/of/www -d www.example.com

This should work just fine even with the NAT domain (and any combination of NAT/non-NAT).

1 Like

Works for me, thanks!

NB: The installer piece was not again executed, probably because an existing certificate was expanded, but that was no problem for me. I cannot confirm, though, that the installer piece works.