Automate update of SSL certificate

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
Hi all,

I've got Lets Encrypt running within a jail using dns-01 to automate SSL certificates for all my jails and their services (this appears to be working quite nicely) I've also got it generating a cert which I would like to use with the FreeNAS GUI.

Given that the generated certs are in a directory such as /mnt/tank/dehydated/certs/freenas/<certs/privkey/etc>

Ideally I want to just replicate what the GUI is doing when you import a new cert/privkey but do so in an automated way via cron. Is there any way to automate updating of the FreeNAS cert via cron task?

From other threads on the forum it sounds like simply copying to /etc/certificates would not be sufficient and I don't like going behind the gui's back really.

I read another post (well bug comment) that uses a tunable to have FreeNAS use a different directory to obtain its certs, but I wouldn't want the tunable to be pointing to a directory within my pool as the pool may not be available at boot time. For example if it's an encrypted pool or there are errors that prevent the pool mounting that need to be solved. The tunable would need to point to a root directory.

I'd be fine with a cron that copied certs to a root directory for example /ssl and then using a tunable to have the webgui use those certs. But wouldn't that be blasted away during any system upgrade? Is there any way to preserve a root directory during upgrades?

Any better ways to achieve this? I get the feeling from the above linked bug post, it may not be possible yet?
 
Last edited by a moderator:

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
Interesting--last I looked, the API didn't support this. I'd think that, to be effective, you'd also need to make whatever API call was necessary to start using the new cert.
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
I'm not sure this will be possible at the moment. I get a 501 when trying to import the cert.

After a little more digging in the docs I found a note that you can use the url "/api/docs" to see the current API for your install. (actually that might be just for websockets).

I'm using 9.10.2-U6 and under the certificates section it looks like only Query is supported, not update/delete etc :(

The api.freenas.org appears to state that it's for 9.10, but unless I've a bug in my post query, I get a 501 not implemented for "https://domain.name/api/v1.0/system/certificate/import/'
 
Last edited:

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
is the api.freenas.org referring to version 11 of freenas or also supported by 9.10.2?
The API changes over time; I'm sure the one documented there is the current version.
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
The API changes over time; I'm sure the one documented there is the current version.

heh you quoted me whilst I was editing :) The docs do indeed say it's for 9.10.

However it returns a 501 not implemented :( Here's a dump of the test code if anyone else wants to give it a go:

Code:

import sys
import json
import requests

PRIVATEKEY_PATH="/mnt/tank/some/path/dehydrated/certs/freenas.example/privkey.pem"
FULLCHAIN_PATH="/mnt/tank/some/path/dehydrated/certs/freenas.example/fullchain.pem"
USER="root"
PASSWORD="somepass"
DOMAIN_NAME="freenas.example"

# Load cert/key
with open(PRIVATEKEY_PATH, 'r') as file:
  priv_key = file.read()
with open(FULLCHAIN_PATH, 'r') as file:
  full_chain = file.read()

# Update or create certificate
r = requests.post(
  'https://' + DOMAIN_NAME + '/api/v1.0/system/certificate/import/',
  auth=(USER, PASSWORD),
  headers={'Content-Type': 'application/json'},
  data=json.dumps({
  "cert_name": "letsencrypt",
  "cert_certificate": full_chain,
  "cert_privatekey": priv_key,
  "cert_serial": 1
  }),
)

# TODO: Check response 201 and activate cert
print r
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
Here's a dump of the test code
What language is that? I'd like to test, but it's not obvious to me. Python, perhaps?
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
Assuming the 501 is an API bug and assuming it's fixed at some point, is there any way to change the active certificate via the API?

Looking in the docs there's http://api.freenas.org/resources/system.html#id11 which looks to cover most of the items on the System/General page, but does not appear to include the "certificate" property?

Please correct me if I've missed something, otherwise I'll look at filing a feature request to get this added.
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
is there any way to change the active certificate via the API?
That would be important. I wonder if uploading a new certificate over the existing one would do that. Otherwise, you'll also need to generate unique certificate names before uploading (and, ideally, delete old certificates).
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
However it returns a 501 not implemented :( Here's a dump of the test code if anyone else wants to give it a go:
Seems to be working for me against FreeNAS 11.0-U2:
Code:
[root@e-smith ~]# ./deploy_freenas.py
<Response [201]>


...and...
upload_2017-9-26_19-59-37.png
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
That would be important. I wonder if uploading a new certificate over the existing one would do that. Otherwise, you'll also need to generate unique certificate names before uploading (and, ideally, delete old certificates).

Yeah I intend to try to get it to just upload over existing if possible and failing that it'd need to have an API to active a new (doesn't exist?) and then delete the old (delete api exists). Not sure if it's possible or not yet thought as I haven't been able to test it :)

Interesting that it works on 11. Is anyone able to test it out using 9.10 to confirm the 501 I see?

When you tried on 11, did it allow you to upload with the same name and overwrite the existing cert?
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
When you tried on 11, did it allow you to upload with the same name and overwrite the existing cert?
No, I get a 400 result when I try that.
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
I've added a feature request for extending the system settings API to allow querying and changing the active certificate.

https://bugs.freenas.org/issues/25990

For a moment I thought the existing "id" property (on the http://api.freenas.org/resources/system.html#settings api) might be referring to the selected certificate ID, but having imported a few certs and selecting a new one, the ID remains at 1 so I'm not sure what property that is actually referring to, docs don't seem to mention it.

Once that exists, I'll modify the python script to handle importing a new cert, activating it then once that succeeds, optionally deleting the old cert. Under the assumption you will only ever want 1 certificate (the latest uploaded/activated one).

There's other variations that would work if people wanted to have more certs and just cycle through a few for Lets Encrypt or manually delete old ones.
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
I've added a feature request for extending the system settings API to allow querying and changing the active certificate.
Setting the active is, I think, the really critical thing. There's no real harm if we can't delete unused certs, just a bit of wasted space in the config file. It's better if we can, of course, but automated deployment can still work without that.
 

gary_1

Explorer
Joined
Sep 26, 2017
Messages
78
Not as of yet no, I'm still on 9.10 and I think the fix is only made in version 11+

I planned to upgrade when 11.1 shipped but reading a few of the issues people had and that the jail upgrade script had slipped to 11.2, I decided to hold off for 11.2

I will take a look once I've upgraded to 11.2 and sorted my jails out though.
 

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
I don't know that I'll be able to code this (I know no Python), but the API documentation has been updated, and there are methods to list certs and to set the active cert for the web GUI. The method to set the active takes as its parameter an integer cert ID, which appears to be a simple counter--the first cert on the system is ID 1, the second is ID 2, etc., and it continues to increment even if certs are deleted (so the ID is never reused). Unfortunately, it looks like the function to import a cert doesn't return the ID of the imported cert. So, it looks to me like the process would go like this:
  • Assign a unique name to the cert (I'd go with letsencryptddmmm or something similarly date-based, representing the date issued).
  • Import the cert using that name.
  • List the certs, and grab the cert ID matching the name we used above.
  • Set the active cert to the ID obtained above.
  • Optionally delete old certs.
Seems conceptually simple enough. The devil will be in the details, no doubt.

FWIW, I ran into trouble with a memory leak on 11.1 causing scrubs to kill the system. It appears to be fixed on 11.1-U1 (and scrubs take 60% less time).
 
Last edited:
Top