Request Let's Encrypt certificate on a port different than 80

Nowadays they configure Apache to some other port and run lot's of different things on port 80 like, for example, Haproxy, Varnish, Pound, Ngingx, etc. And its good and still possible to use Virtualmin's built-in Let's Encrypt certificate requesting provided if a proxy software on port 80 passes Let's Enrypt's web-based validation requests through port 80 to a custom port where Apache resides.

But what if the proxy needs to do something else than passing we requests directly to port 80? Then, unfortunately, you can't use Virtualmin's built in feature, because requesting such an Let's Encrypt certificate through port 80 is hard coded and you'll get:

Parsing account key...
Parsing CSR...
Registering account...
Already registered!
Verifying domain.tld...
Wrote file to /home/core/public_html/.well-known/acme-challenge/DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4, but couldn't download http://domain.tld/.well-known/acme-challenge/DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4
Traceback (most recent call last):
  File "/usr/libexec/webmin/webmin/acme_tiny.py", line 235, in <module>
    main(sys.argv[1:])
  File "/usr/libexec/webmin/webmin/acme_tiny.py", line 231, in main
    signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, args.dns_hook, args.cleanup_hook, log=LOGGER, CA=args.ca)
  File "/usr/libexec/webmin/webmin/acme_tiny.py", line 184, in get_crt
    domain, challenge_status))
ValueError: domain.tld challenge did not pass: {u'status': u'invalid', u'validationRecord': [{u'addressesResolved': [u'xx.xx.xx.xx'], u'url': u'http://domain.tld/.well-known/acme-challenge/DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4', u'hostname': u'domain.tld', u'addressesTried': [], u'addressUsed': u'xx.xx.xx.xx', u'port': u'80'}], u'keyAuthorization': u'DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4.SZ7VZK4_1YBaNFm9CNvSSGw27I41oJ0nQhX4l-g-_Eg', u'uri': u'https://acme-v01.api.letsencrypt.org/acme/challenge/HkYJdvACYdwrH5G_N9hGSFuySPwyU4-LmBDQGj3Z-1k/1906333853', u'token': u'DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4', u'error': {u'status': 400, u'type': u'urn:acme:error:connection', u'detail': u'Fetching http://domain.tld/.well-known/acme-challenge/DXAmuFX1cbDx93unCqhM_pQonwb4kOlPNmIOa4EiMk4: Timeout'}, u'type': u'http-01'}

Let's encrypt community seems to have already found a solution for this issue. Per thomas' proposal on https://community.letsencrypt.org/t/le-client-needs-to-bind-to-port-80-w...

  1. make sure apache modules mod_proxy and mod_proxy_http are enabled a2enmod proxy proxy_http
  2. add the following to the mod_proxy config file (usually /etc/apache2/mods-enabled/proxy.conf) <IfModule mod_proxy.c>
    ProxyPass "/.well-known/acme-challenge/" "http://127.0.0.1:9999/.well-known/acme-challenge/" retry=1
    ProxyPassReverse "/.well-known/acme-challenge/" "http://127.0.0.1:9999/.well-known/acme-challenge/"
    <Location "/.well-known/acme-challenge/">
    ProxyPreserveHost On
    Order allow,deny
    Allow from all
    Require all granted
    </Location>
    </IfModule>
  3. if you have a local firewall running: make sure it won't block access from localhost to localhost:9999
  4. run letsencrypt-auto with the http port set to 9999 letsencrypt-auto --agree-dev-preview --agree-tos --renew-by-default --standalone --standalone-supported-challenges http-01 --http-01-port 9999 --server https://acme-v01.api.letsencrypt.org/directory certonly -d your-domain-here.tld

I wonder is it possible for Virtualmin to take care of the step 4 above and thus make Virtualmin even better by allowing requesting Let's Encrypt certificate through Apache port different than 80?

You already have an option to indicate a custom Apache port in Virtualmin's configuration page. So all that needs to be done is to check if a Virtualmin admin set custom port and if s/he did then to re-write the Let's Encrypt request using different port.

Status: 
Active

Comments

Body: View changes

Additionally, I noticed Virtualmin's implementation tries (1) DNS-based and (2) web-based verification through port 80 only, whereas Let's Encrypt can verify also through port 443. So why only two methods when there is one more?

I really really wish the Let's Encrypt service supported ports other than 80 and 443 :-(

In your specific use case, do you have a different webserver on port 80 forwarding to Apache on some other port? If so, the solution would be to configure that webserver to pass on requests to /.well-known ... in which case, Virtualmin's existing behavior should work fine.

Yes, another proxy server is on port 80 and in fact we have already configured it to pass requests to /.well-know and it is working ok. Can you elaborate why Virtualmin runs only two checks on port 80 and DNS-based one, why it doesn't check on port 443 too?

Related issues:

Jamie, can you incorporate step #4 as the rest could be done by post installation script?