Let's Encrypt, DNS challenge, and scripting?

I have my FreePBX 15 system behind a firewall, and have no intention of opening port 80 to the world as Let’s Encrypt has always required–unfortunately, this means I can’t use the built-in certificate management to obtain and renew a cert from Let’s Encrypt. However, I’m entirely comfortable with the DNS challenge; I’m using that to get certs for probably a couple dozen devices on my LAN. I only found one earlier topic (Let's encrypt dns challenge) discussing this, but that left it at “no, FreePBX doesn’t do the DNS challenge.” Well and good, but is there any other way to automate the process?

It’s relatively trivial to install acme.sh, certbot, or some other client and get my own cert using DNS validation–I’ve done that, in one form or another, on many systems. Automating renewals is similarly trivial–as long as your DNS host supports it (I like acme-dns for this purpose). What I’m not clear on is what, in a FreePBX context, needs to be done with this cert.

  • Is there some kind of API call I can make to import the cert? I’d think this would be preferred; it would then show up in the Certificate Management page and be assigned to any services that would use it (which are…?)
  • Or is it enough to just copy the cert to where Apache expects it to be and reload Apache? That’s obviously simple, but if the cert’s used for anything else, it wouldn’t do that.

Where all is the cert used, and what (if anything) can be done from the CLI to install a new cert?

Cert Man stores all certificates in /etc/asterisk/keys (configurable in advanced settings) and the default cert in the /integration folder there. I haven’t tried this, but you could create a self signed cert in certman, and then overwrite the files generated with your own files, so there would be a GUI entry you can use. You can also use the CLI to set the default cert, so every time you update the cert files externally, you would run fwconsole certificate --default=x to update the default cert files.

There is also these custom firewall rules that allow world access to the LE validation files, without allowing world access to anything else: Let's Encrypt Certificate renewals failing

1 Like

For ease of use I recommend acme.sh from Neil Pang

curl https://get.acme.sh | sh

Ultimately you need the .crt and .key in both your apache2/httpd 443 server config, and for tls simple you need them also in /etc/asterisk/keys (and perhaps /etc/asterisk/keys/integration)

I find the easiest route is to put them in /etc/asterisk/keys and have your webserver refer to that location when looking for certs,

#delete everything in /etc/asterisk/keys/
#add your creds into acme.sh.env and ‘source’ it
#have letsencrypt issue a cert using your preset auths

you might want to RTFM from Neil as to what DNS services are supported ( 100+ ) and what environmental variables need to be in place

acme.sh --issue --domain your.tld --dns dns_whateveryouuse

#install the crt and key (and . . .) to a FreePBX approriate place

acme.sh --install-cert -d your.tld --cert-file /etc/asterisk/keys/${hostname -f}.crt --key-file /etc/asterisk/keys/${hostname -f}.key --reloadCmd 'systemctl restart httpd/apache2 ’ --fullchain-file /etc/asterisk/keys/${hostname -f}.pem

have FreePBX use the newly installed cert and put the softlinks into “integration”

fwconsole certificate --import

next lien will work because you deleted anything there previously, right?

fwconsole certificate --default=0

There will be a cron job up and running, you can run

acme.sh --cron

at any time to see WTF

1 Like

I use acme.sh quite a bit, though the hook script for certbot does a much nicer job of integrating with acme-dns–but that part’s covered in any event.

Would these need to run at every renewal? If so, it would seem like setting up a simple script to handle those and the Apache reload would be the way to go.

…which would be some fairly simple edits to /etc/httpd/conf.d/ssl.conf, I’m thinking, unless it needs to be specified elsewhere. Is there a GUI way to implement a HTTPS redirect, or just mess with the config files directly?

Personally I find acme.sh perfectly fine ,

I have a $5 DigitalOcean server that handles hundreds of certificates on almost as many machines including exotics like mikrotik and asus routers using acme-sh --deploy generally over ssh

Once setup they are pretty hands off , perhaps a --post-hook “…&&…” for the fwconsole stuff would not be inappropriate for FreePBX , I don’t find I need it As to the reload command , I already had
–reloadCmd 'systemctl restart httpd/apache2 ’ of course the / bit is an and/or

As to the web server, If you use sysadmin it would be done there but I don’t, for any other webserver there are a hundred google responses to suit, but basically

grep -ir ssl /etc/httpd/* # for RH
grep -ir ssl /etc/apache2/* # for Debian
grep -ir ssl /etc/nginx /* # for us nginx guys on any OS

would expose an enabled or default web site that needs massaging, this is generally a one-time thingy.

(pretty sure none of this is gooey friendly :wink: but you only have to sweat it once )


I remember a little cheat I did when I was a newby , enable port 80 for a couple of minutes, use --apache or --nginx but not --dns when issueing a cert ( you can even use --test) and have acme.sh do the grunt work for you after you say ‘yes please’

Interestingly, no–even though I deleted everything in /etc/asterisk/keys, entry 0 was still present as the default self-signed cert–so I set --default=1 instead, and that seems to work.

Also, because my distro uses Apache 2.4.6, I needed the CA cert to be specified separately. My resulting installation command was acme.sh --install-cert -d $(hostname -f) --cert-file /etc/asterisk/keys/$(hostname -f).crt --key-file /etc/asterisk/keys/$(hostname -f).key --reloadcmd 'systemctl reload httpd && fwconsole certificate --import && fwconsole certificate --default=1’ --fullchain-file /etc/asterisk/keys/$(hostname -f).pem --ca-file /etc/asterisk/keys/chain.pem.

…and after specifying webserver.crt, webserver.key, and chain.pem in /etc/httpd/conf.d/ssl.conf and setting the server name, it’s up and running on HTTPS with a trusted cert. Excellent. I’ll deal with the HTTPS redirect in a little bit, but that shouldn’t be too difficult.

1 Like

Well done I forgot the

fwconsole certificate --updateall

in the my steps, it will then be just bitch but ‘fix it’ anyway

Yes the redirect is trivial just have the server on 80 redirect with a 301 (permenant) to your https service, presumably on the same machine.

It’s a little unclear what would be the best way given the various config files, but I did it by creating /etc/httpd/conf.d/redirect.conf with the following contents:

<VirtualHost *:80>
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]

It’s redirecting, and not throwing errors, so I’m taking that as a good thing. I don’t see a default virtual host anywhere else to put the redirect in.

Totally depends on your webserver, as previously stated i’m an nginx guy, but whatever is needed to send a valid 301 to any attempt to :80 is good.

With acme-dns, for a single FQDN, it works just fine. The workflow is a little clunky (mainly in that you need to generate your own credentials and specify them with the appropriate environment variables), but yes, it works. For more than one FQDN, acme.sh is simply unusable with acme-dns, since it can only use one set of credentials, and acme-dns only allows a single FQDN (and its wildcard, if desired) with a given set of credentials. Using the hook script for certbot, all the credentials are obtained and managed automatically, which is more convenient even for a single FQDN, and you can also generate multi-SAN certs which wouldn’t be possible with acme.sh.

For this particular application, one FQDN is all that’s needed, and acme.sh is a much simpler and more lightweight ACME client–I’ve long criticized certbot for being a tangled mess of dependencies that tries to do way too much in being “SSL for dummies”.

Now, if you aren’t using acme-dns for your validation tokens, this is more or less moot–my Cloudflare API key, for example, handles any domains on that account.

I don’t find that that to be true with acme.sh using for example --dns dns_dgon (Digital Ocean) and a
export DO_API_KEY=“779729b420a6c10sdllsdf8sdf9879sdgsdgdgbd27012d803579313fa9”
in acme.sh.env , you can have have as many --domain a.b.x --domain *.b.x --domain c.d.e as you want in one --issue request , they will all be issued in sequence, with the DNS-01 challenge being individually checked against the name service, each set of certs will end up in the relevent …/acme.sh/x.yz directories, (wild cards being concatenated with specifics as appropriate) .

Of course both cloudflare and DO are covered along with 98 other common name services. Self hosting your namservice is possible but of course you will have to write your own script to bypass any manual clunkiness as you add and remove the TXT records.

For almost anyone else here, transferring your Nameservice to cloudflare is about as cheap and simple as anything you will find anywhere if you are not already covered.

No, I wouldn’t expect so. I think the issue is pretty much specific to using acme-dns:

That system, by design, uses limited credentials–if a set leaks, in the worst case, the attacker could issue a cert for a single FQDN. By contrast, if my Cloudflare Global API key leaks, an attacker could issue for any domain I own, or even transfer all my domains to his control. But because of the limited scope of the credentials when using acme-dns, you need a different set for each FQDN, and acme.sh just doesn’t support this.

Agreed, especially since “cheap”=“free”.

Anything can leak, but things like FreePBX have notoriously leaked in the past and might well do so in the future, the great thing about DNS-01 is that the you can do it from anywhere, the CREDS are as secure as you want them to be , you can keep them elsewhere like bitwarden and get them when needed then , get the updates and deploy to your servers as necessary, yes sounds paranoic but seriously how is that ever worse than using an arbitrarily open server on port 80 that the whole world can read and write, no matter how well protected you are told it is.

I believe certbot supports dns now right? Assuming your provider is a big name that has an API that is.

The problem with FreePBX is the annoying need to copy post update because certificate manager does not accept linked files.

@dicko and I tried to figure that out unsuccessfully in this thread.

Oh I also had to reselect the cert in sysadmin

Really annoying.

Hehe, I am inclined to “re-tweet” your:-

“Why even bother to reply when you have no clue? All you did was confuse the issue.”

But I won’t, (oh, I guess I did anyway :wink: ) this thread has nothing to do with sysadmin, certbot or analogic/lescript, currently none of which can get the job done without exposing :80 to the internet.

We are discussing a working solution for successfully installing certs using DNS-01 and acme.sh that wraps up the issuing of certificates and deployment into the FreePBX infrastructure outside of FreePBX. It is both functional and easy to implement.

Defensive much? That’s all I"ll say on that point.

Whether you use acme.sh or certbot the issues are the same. FreePBX (the distro) is a disaster for dealing with certificates if you go outside of the GUI.

You are even unable to solve everything with the built in CLI tool (fwconsole). You are forced to wander all over the place copying certs with new names.

  1. However you get the LE cert, it needs to end up in /etc/asterisk/keys
  2. Then you need to use fwconsole to import it.
  3. Then you need to still copy things around or use SysAdmin (pro not required) to aply the certificate to Apache. I forgot about fwconsole sysadmin updatecert

Again we will have too agree to disagree ,

No it doesn’t have to end up in /etc/asterisk/keys , its just the conventional location and suits FreePBX.
FreePBX uses the “fwconsole certificate --import” function to normalize certs and keys into both the infrastructure of /etc/asterisk/*.conf and updates softlinks in /etc/asterisk/keys/integration but that is for TLS and WSS connections so we choose that for the webserver to use as a convenient point of reference, The webserver whether it be apache2 or httpd or nginx needs to know (just once) were to reference the certs, this is not brain surgery nor particularly difficult to do. (As an added bonus we show how to 301 :80 to :443 )

So everything you are furious at is covered to some extent in this thread, ultimately I pretty well disagree with every one of your points. And if you can put aside your ad hominem rage can I ask again:-

“And your solution is?”

This is for a site where I don’t have access to the DNS, so it has to accept port 80. but certbot can handle DNS challenge and canbe easily modified.

sudo certbot renew --pre-hook "fwconsole firewall stop && systemctl stop httpd" --post-hook "systemctl start httpd && fwconsole firewall start"

export FQDN=`hostname -f`
sudo cp /etc/letsencrypt/live/$FQDN/fullchain.pem /etc/asterisk/keys/$FQDN.pem
sudo cp /etc/letsencrypt/live/$FQDN/chain.pem /etc/asterisk/keys/$FQDN-ca-bundle.crt
sudo cp /etc/letsencrypt/live/$FQDN/privkey.pem /etc/asterisk/keys/$FQDN.key
sudo cp /etc/letsencrypt/live/$FQDN/cert.pem /etc/asterisk/keys/$FQDN.crt

sudo fwconsole certificates --updateall
sudo fwconsole sysadmin updatecert

One of these days I will put it all in a script to call in the post-hook and then call the certbot renew with a systemd timer.

While yes, you can put thing wherever you want, going outside the box is a bad thing to support going forward. It is always better to have things be as default as possible. You will not always be the person looking at the PBX. The next person may not know everything you know. So every single thing outside of hte box is a complication that will cause potential problems.

It would of course be best for FreePBX to support some kind of intelligent LE method instead of the current scenario.

Please do but until you have done that, bear in mind that the thread you just “contributed” to had the opening sentence of:-

“I have my FreePBX 15 system behind a firewall, and have no intention of opening port 80 to the world as Let’s Encrypt has always required–unfortunately, this means I can’t use the built-in certificate management to obtain and renew a cert from Let’s Encrypt.”

From this sprung a comprehensive discussion of how to do just that with an apparently acceptable solution, I just can’t see how you think your posts have been in any way apropo.

Sorry, sysadmin not allowed in this thread (we are in “general help” :-), some of us use Debian, raspberries even ) try again . . .

but fwconsole certificate --updateall is sufficient for us “great unwashed”