Upload ssl certificate to pfsense via ssh and restart webconfigurator

I use a seperate server that handles all my LetsEncrypt certificate renewals, this gives me a central secure location to create and renew certificates (all renewals are done via DNS, so the renewal server is not accessible from outside), these are then uploaded via ssh to various servers and then apache/nginx/postfix etc is restarted on the remote systems.
I can not find where pfsense stores the certificate used for the web frontend :frowning:

My normal way of renewing a certificate is :
SSL Managment server :-

certbot /renew
scp manager@remote.server.one /etc/letsencrypt/live/remote.server.one/fullchain.pem /etc/ssl/
scp manager@remote.server.one /etc/letsencrypt/live/remote.server.one/private.pem /etc/ssl/
ssh manager@remote.server.one /usr/sbin/service nginx reload

Can anyone point me in the right direction? Also how can you reload the web frontend from the command prompt?

As I already have my ssl management server setup, I don’t want to use the letsencrypt package on pfsense itself and I want to use the same system to push certificates to multiple pfsense servers.

Any tips/suggestions would be welcome.

Interesting question, but I have no answer.

I do know that within pfsense is just a file system the same as any other computer, have you enabled the pfsense SSH service yet? https://docs.netgate.com/pfsense/en/latest/recipes/ssh-access.html

There is also a sudo package you can install if you need “more power”. https://docs.netgate.com/pfsense/en/latest/packages/sudo.html

pfSense has an acme package for using LetsEncrypt, and if you ssh into your pfSense option 11 is Restart webconfigurator

Hi Greg_E, yup, already have full ssh access to my pfsense box, I use this for custom config backups etc and for remote support (via ssh login with keys).
The config.xml file does contain a copy of the SSL certificate but on testing (by inserting random chars into the config entry) this did not affect the web frontend after restarting webconfig.
Looking at nginx’s config, it appears to use :

/var/etc/cert.crt
/var/etc/cert.key

for the https certificate, but editing this file doesn’t appear to make any differance and this file gets overridden on webconfig restart.
From reading through the webconfig restart script (and /etc/inc/system.inc file), the cert.crt & cert.key files are created from the config.xml contents, but if i insert a random char into the certificate entry in config.xml, this doesn’t seem to affect the webconfig! :frowning:

So I am at a loss as to how to push new certificates to my firewall :cry:

sdfungi, yes there is an acme package for LetsEncrypt, but i already have a secured system that generates my certificates and manages all of my other servers, also I am reluctant to add something to my firewall that could potentially grant someone access to my DNS servers if they managed to hack my firewall (my SSL certificate box is totally locked down with no inbound access at all, requires physical access to get a terminal and uses ssh outbound only to push certs out.

Thanks

I understand what your asking for and what I’m proposing doesn’t address what you need. Why don’t you just let pfSense manage its own certificate via the built it WebGUI. It’s really good at renewing itself and I haven’t had the process fail.

I do understand this takes away from your concept of centralized management – sorry. Just curious the process you are using for certificate distribution – are you using ansible or other automated method such as a bash script with scp/ssh? Does your script run via post -renew hook?

Hi Kevdog,
As I use DNS-01 to generate my certificates and most of them are subdomains, I created a set of script that work as follows :
server1.conf :-

DOMAINS=‘test1.example .net, test2.example .net’
SERVER=‘server1.example .net’
SERVICES=‘nginx,postfix,dovecot’

The above would create/renew a certificate that had test1 & test2.example .net in it, then upload this to a server at server1.example .net, then restart the services nginx, postfix and dovecot.

Every week, my scripts check the config folder, for each .conf file :

Extract first domain from DOMAINS and check if there is a folder ‘/etc/letsencrypt/live/DOMAIN’, if folder doesn’t exist, then request a new certificate via DNS-01
If folder exists, then attempt a renewal for the certificate via DNS-01.
If certificate has changed, check what services are listed, if using postfix/dovecot combine certificates into a format that is compatible with those services.
Push all certificates to appropriate folder for service (via scp) (creating a copy of previous files before overwriting)
Restart each service (via ssh).

Using this system means only this one box has the API keys to access my external DNS servers, no ports need to be opened into my internal network and I have a central location to edit files etc (great for when things happen like LE having to bin all there certificates, like back in March 2020) also means that I only have to update my acme scripts in a single location.
Thanks

Hey it would be nice to look at your script – I like your method. Seems like some checking has been built into your script and such. I started making an anisble script for mine. In terms of checking if cert had been renewed, it calculated a hash sum for the certs pre and post renewal, and if the sums differered it would attempt scp transfer. It would cp the old files prior to transfer and then scp the new certs and then recalculate hashes to ensure the certs had then been transferred appropriately. It would then restart nginx/apache. I don’t remember what I had to do for postfix (I don’t use dovecot). Anyway - sounds like your script does something similar.

I’m sorry I can’t help you with pfsense. There probably is a method, however even if overwriting the appropriate files in the filesystem, you’ll probably have to trigger an API call to have the changes be reflected in the GUI. I do something similar for FreeNAS via a @danb35 script, which needs to call the middleware to appropriately import the certificate. Possibly the FreeNAS forums could help you out on this issue.

Hi kevdog,
LOL, yours sounds a lot more reliable. I just use last modified time to check if a cert has been renewed.
I also had a script that pushed a couple of local files to the remote system, so they could be run by myself on the remote system that automatically setup the required user (‘manager’ in my example above), prompted for what services I was going to config with certs and configure scripts to restart each service and add manager to the sudoers file with no password access to the restart scripts. But I am now trying to find a way to automate this bit as well, probably something like this :
SSL Management server :-

newcert -D primary-domain -d additional-domain -e additional-domain -s server-address -S postfix -S nginx -S dovecot

This would then :

Call the inital letsencrypt new certificate command
Create certificate sync file server.conf that contains the data as passed from the newcert command
Create management script (configured as per services listed in ‘newcert’) and setup script
Prompt user to scp/rsync both files to remote system
Wait for confirmation of transfer
Sync certificate to remote system via SCP
Call management script on remote system via SSH

The setup script would be run on the remote system once, this sets up a dedicated user to manage the certificate sync/restart, moves the management script to the correct folder and then adds the new user to sudoers to allow them to run as root without any password (maybe with some for of sha/md5 checksum prior to calling management script to check script integrity).

After a cert sync the management script would be called via SSH with a sha/md5 checksum as a single param and dp the following :

Check the sha/md5 checksum of the certificate files uploaded, these are just uploaded to /home/USER/
Create any additional files as required by some services (IIRC postfix or dovecot need cert files combined in some form, its been awhile since i set one up)
Copy original certificates to backups
Copy new certs to correct folders
Restart each defined service (if one doesn’t restart correctly, possibly revert cert and try again?)

What do you think?
All of the remote server stuff would have to run under bash (or sh, but for me all my systems have bash as there default shell) to avoid having to install something like Python or PHP.
I also want to look at adding the ability to push certificates to VM/Docker/OpenVZ/Chrooted systems and call the appropriate service to allow these to be restarted.
Also need all certificates to be placed in there ‘normal’ folders as expected by each service to avoid any issues with upgrades or migrations etc.

Yea I think the basics of your script design are totally on the money. The problems I always run into is error checking – meaning for example – if I execute a command and something happens that I’m not expecting, how do I deal with the issue. How am I notified of the return code of such a command? What are the options for dealing with nonzero return codes? Beyond error code, I thing the creation of your management script is going to be the most difficult part of your design – need to parse out the services and then based on the services found, create the management script. I often wonder however the design of the whole “centralized certificate renewal” since this of course then demands a method to distribute the certificates. Sometimes I wonder if its just easier to let each individual system manage their own renewals via acme and restart their own services based on a post-renewal hook. I can see the downside of this strategy as well however.

Hello, check out this git repo for a bit of php code that looks like it will help you do what you need. It says it will import a certificate into the config.xml, and restart the web ui. I’m giving it a try now.

I’m trying to do the same thing as you, I have 30 pfsense boxes and I want to push out certificates from an already existing central letsencrypt repo. I don’t want to manage Acme on 30 different firewalls individually.

I tested the repo I linked to, and it did work fine, but there was one issue with the script adding a gui alert because of how it writes the config on 2.5+/21.05+ so here is a new repo that I forked and added some fixes to.