Nginx with `tailscale cert` -- automatic renewal?

Hi all,

I’ve just recently starting tinkering with nextcloud. I only access the machine hosting nextcloud via tailscale (in large part because I live in a very rural location in which Starlink – and its CGNAT – is my only real option for internet over 10 mbps). It seems that some services in nextcloud don’t work well if not provided over https (for example configuration profile generation for my iOS devices).

The nextcloud module has good support for nginx (with some ongoing work to support others). Nginx normally is able to get certs via acme / letsencrypt, but unfortunately this process fails for me because I don’t have a unique external IP address, and nginx doesn’t seem to be able to get a cert over tailscale. (Caddy has ?experimental support for this.)

I am able to manually generate a cert via the tailscale cert command, but these certs don’t automatically renew.

I figure I could probably just make a systemd script that periodically runs tailscale cert, copies the certs and changes permissions to be accessible by nginx, but I wanted to see if others had any other recommendations or solutions for this issue.

It certainly would be nice if nginx could just manage the tailscale certs the way it does letsencrypt certs, but I wouldn’t know where to start on this.

For the moment, a workaround seems to be using tailscale funnel to briefly expose port 80 to the internet during cert establishment / renewal. My current workaround is to do so by adding an ExecStart{Pre,Post} to the service file that is created by security.acme.

It seems to require the + prefix to run those commands with full privileges (because the rest runs as the acme user by default).

Obviously if people are using funnel for anything else, this would disrupt their connection; I don’t see a way to stop serving only a single port. As an alternative, one could probably create a separate service that runs funnel in the foreground (by omitting --bg) and include RequiredBy=acme-mymachine.my-tailnet.ts.net.service and then systemctl stop that service in ExecStopPost. Or something like that.

systemd.services."acme-mymachine.my-tailnet.ts.net" = {
  after = [ "tailscaled.service" ];
  requires = [ "tailscaled.service" ];
  serviceConfig = {
    ExecStartPre = "+${pkgs.tailscale}/bin/tailscale funnel --bg 80";
    ExecStopPost = "+${pkgs.tailscale}/bin/tailscale funnel reset";
  };
};