Multiple letsenycrypt clients using port 80 at different times - automatic reconfiguration possible?

Hello,
I’m curious to learn if I can configure RouterOS so that it changes a port redirection on the fly, i.e. depending on the last host that called a specific domain or IP address.
Setup: I’m running two virtual machines on a server behind my RB4011. NAT is activated on MikroTik and DDNS service is pointing to it. For this purpose, vm1.example.com and vm2.example.com point to the same DDNS-address. Each VM has its own local IP address and is running a webserver on port 80.

Internet --> RB4011	--> VM1 192.168.88.101
			--> VM2 192.168.88.102

What I try to achieve: both VMs should be able to renew their letsencrypt certificates via http-challenge with my manual interaction needed. We can assume that both VM letsencrypt requests never occur at the same time.
My idea:

  1. When VM1 is trying to connect to domain D, RB4011 is marking the connection with VM1.
  2. Automatically delete old rules
 /ip firewall nat remove [/ip firewall nat find where dst-port="80"]
  1. NAT rule to redirect incoming traffic at port 80 to VM1
/ip firewall nat add action=dst-nat chain=dstnat comment="letsencrypt http redirect" connection-mark=vm1 dst-address=192.168.88.1 dst-port=80 protocol=tcp to-addresses=192.168.88.101

(this code-tag does not format correctly, no idea why)

The idea fails for two reasons at least:

  • I could not figure out how to set the appropriate IP addresses per VM in NAT rule without script.
  • Removing old rules by script seems not to be possible, found here.

Would be nice to learn more about the mechanisms to achieve what I described.
Maybe someone has an idea and can point me in the right direction.

Thanks in advance and best

I usually switch to dns-01 challenge if the machine is not reachable (either directly or via reverse proxy). Is this an option?

Same here (about DNS challenge). Otherwise you need either different ports or different public IP addresses.

Dear @nescafe2002, unfortunately, I cannot access the DNS server via script. My provider does not allow automatic interaction.

Dear normis, what do you mean with “different ports”? The letsencrypt http challenge can only use port 80 or 443. Or did you mean different ports for the VMs?

Thanks & best

My mistake.

But it seems that what you want is not suggested or supported:

https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443-v3/63770/9

Dear normis, there might be a misunderstanding. I want to use port 80 and not a different port.
It’s just that I have more than one webserver behind my MikroTik devices to redirect the traffic to. If I had a single webserver that should use letsencrypt http challenge, I could activate a simple port redirection, right?

Another idea that comes to my mind as I write:
Run a script that switches port 80 redirection between the virtual machines. For instance, odd days VM1 and even days VM2.

Thanks and best

I was talking about using certificate in a private network, see the link and the discussion

You can point a CNAME to a self hosted ACME dns server. This works quite well for me for several projects.

e.g.

To set up an ACME server on auth.domain.com:

auth.domain.com NS auth.domain.com.
auth.domain.com A 1.2.3.4 [your-public-ip]

ACME-DNS: https://github.com/joohoi/acme-dns running in a docker container.
Public port 53/udp is forwarded to ACME-DNS.

To allow challenge on domain.com and all subdomains (or wildcard)
_acme-challenge.domain.com CNAME [random key].auth.domain.com

To allow challenge on subdomain.domain.com or *.subdomain.domain.com only:
_acme-challenge.subdomain.domain.com CNAME [random key].auth.domain.com

The random keys are generated by ACME-DNS.

@normis
Oh, thanks for clarifying.

@nescafe2002
Interesting idea, thanks. I will look into it.

I’m for DNS too. But if you want it really simple and static and don’t mind one VM depending on the other, there’s also another way.

Just forward port to one VM, let’s say VM1. This one won’t have any problem. When VM2 tries to renew certificate, LE will try to verify challenge and it will connect to VM1 where it won’t find it. But you can configure VM1 to act as proxy and get the file from VM2, using either server’s proxy functionality, or with some script.

For example, in few places I have this as Apache config:

Alias "/.well-known/acme-challenge" "/path/to/challenges"
<Directory "/path/to/challenges">
    AllowOverride None
    Require all granted
    Header set Content-Type "application/jose+json"
    #
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^[a-zA-Z0-9\-_]+$ /.well-known/acme-challenge/le-proxy.php [L]
</Directory>

First half is normal config for LE and second with rewrite is for proxy. Php script is simple:

<?php
  $data = file_get_contents('http://<hostname_of_VM2>'.$_SERVER['REQUEST_URI']);
  if($data === false) {
    http_response_code(404);
  } else {
    echo $data;
  }
?>

Dear sob, that’s an interesting approach. Thank you.
If I’m not mistaken it may even be possible to setup a static proxy redirect via apache2 domain conf file. I once had this kind of configuration to proxy database access via webserver to the database server.
I’ll definitively look into it!

Thanks again & best

I don’t have much experience with Apache as proxy, it depends if you can do the same config as rewrite in example has, i.e. local files need to be served for VM1’s use and only when requested file doesn’t exist, it must be taken from VM2.

I’m using an Apache2 proxy to pass the let’s encrypt http challenge to the appropriate host(s). Here for the apache2 domain config for the first VM. Possibly I could use RewriteRules to automate the ProxyPass target but that’s a challenge for another day.

<VirtualHost *:80>
    ServerName vm1.example.com

    # everything not the let's encrypt http challenge is forbidden
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/.well-known*
    RewriteRule (.*) "-" [F]

    ProxyPreserveHost On
    ProxyPass / http://vm1.example.com:80/
    ProxyPassReverse / http://vm1.example.com:80/
</VirtualHost>

To add some extra security, I may add pre/post hooks to the certbot renew statement to enable/disable the RB4011 firewall rule that forwards the http requests to the Apache2 proxy.

Thank you all for the input!