A recent project gave me an opportunity to try out Certbot on Windows. As I've written about before, I've had an extensive journey with Certbot, at times in fairly 'non-standard' configurations, and Certbot on Windows is no different. Fortunately in my circumstance I am using Apache on Windows and not IIS as the latter requires a different approach (and it would appear a different ACME client built to interact with IIS).
I was able to follow the simple instructions for Certbot on Apache/Windows to get Certbot. I let Certbot install at the default location (on the C drive).
Obtaining a Certificate
Requesting a new certificate was really as 'trivial' as running certbot certonly --webroot
from an elevated command prompt. I chose to use webroot
as it was the 'least intrusive' way to handle the validation.
The first run on a new installation of Certbot presents additional prompts to 'register' and whatnot; subsequent invocations do not prompt in this way. The key prompts are the domain(s) for which to request a certificate and the webroot
path, which in my case was on the D drive—the path of the DocumentRoot
directive in the vhosts.conf
, e.g. D:\project\files\web
.
Within a couple seconds I had a new certificate available at the installation path for Certbot (in the C:\Certbot\live\
directory, a path structure similar to Certbot on Linux).
Certificate Installation Process
Using the webroot
plugin doesn't automatically change your Apache conf
files, so once I had a new certificate in place I needed to add this information to the vhosts.conf
configuration file. In this case, it was more or less a matter of copying the non-SSL (port 80) VirtualHost
declaration, changing it to port 443, and adding the SSL certificate information and extra configuration directives, like this example:
<VirtualHost hostname.tld:443>
DocumentRoot "D:\project\files\web"
ServerName hostname.tld
ServerAlias www.hostname.tld
SSLCertificateFile "C:\Certbot\live\hostname.tld\fullchain.pem"
SSLCertificateKeyFile "C:\Certbot\live\hostname.tld\privkey.pem"
</VirtualHost>
In testing I was having quite the time getting SSL to 'behave' on Apache/Windows out of the box. I would attempt to [re-]start Apache and get a critical error. The Apache error log wasn't terribly helpful, nor were the httpd.exe -t
or httpd.exe -S
configuration test commands. I only knew that something with the SSL directives wasn't playing nicely.
As a result, I copied a portion of the automatically-created Include /etc/letsencrypt/options-ssl-apache.conf
file from an Apache/Linux box and used it as an include on Windows, which I named letsencrypt-ssl-apache.conf
. Voila! So something in the default configs just doesn't quite play nice with Certbot certificates out of the box, but this config (also a Gist) is an example of how I made it behave:
#
# LET'S ENCRYPT SSL EXAMPLE SETTINGS
#
# This include was copied from a functioning Certbot installation on Linux.
# Its presence makes the SSL certs issued by Certbot behave on <Windows>
#
SSLEngine on
# Intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLOptions +StrictRequire
I updated the VirtualHost declaration with the Include directive:
<VirtualHost hostname.tld:443>
DocumentRoot "D:\project\files\web"
ServerName hostname.tld
ServerAlias www.hostname.tld
Include conf/letsencrypt-ssl-apache.conf
SSLCertificateFile "C:\Certbot\live\hostname.tld\fullchain.pem"
SSLCertificateKeyFile "C:\Certbot\live\hostname.tld\privkey.pem"
</VirtualHost>
This modification allowed Apache to load, but the error log indicated a suggestion/problem with the SSL cache, which I had not configured. Fortunately it was fairly straightforward and made the error go away while also improving server performance.
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
...
# Secure (SSL/TLS) connections
#Include conf/extra/httpd-ssl.conf
SSLSessionCache "shmcb:D:/path/to/logs/ssl_gcache_data(512000)"
SSLSessionCacheTimeout 300
Renewals & Buttoning Up
I always like to test the renewal on a fresh cert, so certbot renew --dry-run
is a familiar command for me. As expected, a test renewal was successful, so the automated renewal process should behave without any problem. Hooray!
What About that Renewal Job?
I was curious how the renewal scheduled task would work, so I kept an eye on the scheduled tasks list. Certbot creates a scheduled task to run twice daily (at ~12 hour intervals with a random offset). I noticed, however, that over the first few days it was not actually ever triggering. Digging into the task details, I saw the issue pretty quickly.
The scheduled task is set to run as anyone in the Administrators
group, but only if an administrator is logged on at the trigger time. Bummer. This would be a problem for my production environment, because Apache is running as a service and for the vast majority of time nobody is logged on to the server in question, so renewal actually being triggered would be hit or miss at best and that's not a plan.
Fortunately there's a relatively simple fix for this default behavior: we use a "headless" (e.g. designed to run such jobs, can't do console login, and can be a member of the Administrators
group) service account. Change the scheduled task to run as this user, with saved credentials, and run whether or not someone is logged on. Thus far it seems to be triggering as expected, though it is too early to actually see a renewal process as (as I write this) 60 days have not yet passed since the initial certificate request and installation.
Restarting Apache
Since the renewal job will run with the webroot
plugin, we need to use the --post-hook
argument on the renewal command to D:\path\to\bin\httpd.exe -k restart
. This could theoretically be accomplished with the --deploy-hook
as well, but I chose --post-hook
since a momentary Apache restart is not a service issue for end users due to traffic volume. There is also conflicting information whether not not --deploy-hook
will behave with a direct command (versus a path to a script), and I didn't try that angle. For a high-volume service/site, --deploy-hook
might be a more robust solution, in combination with a more defined renewal schedule than the defaults.
If you specified all of the arguments such as --webroot-path
and --post-hook
(or --deploy-hook
) out of the gate (at original request), renewal modification is not necessary. I, however, needed to ensure both the webroot path and the post hook were added to the renewal configuration, necessitating a command line modification (recommended over manually editing the Certbot renewal configurations).
Modifying the renewal configuration is handled by manually running the renewal at the command line (first with a --dry-run
):
certbot renew --cert-name hostname.tld --webroot-path "D:\project\files\web" --post-hook "D:\path\to\bin\httpd.exe -k restart" --dry-run
If the test is successful, running a 'forced' renewal to codify the changes is appropriate:
certbot renew --cert-name hostname.tld --webroot-path "D:\project\files\web" --post-hook "D:\path\to\bin\httpd.exe -k restart" --force-renewal
Examining the hostname.tld.conf
Certbot renewal file illustrates the changes were pushed through:
# Options used in the renewal process
[renewalparams]
account = lonstringofseeminglyrandomchars
authenticator = webroot
server = https://acme-v02.api.letsencrypt.org/directory
post_hook = D:\path\to\bin\httpd.exe -k restart
webroot_path = D:\project\files\web
Loading the production website in a browser you should see the new certificate being presented. I just checked the issue date to verify.
Easier Than Expected
Having skimmed some of the Certbot on Windows documentation in the past, I was a little skeptical about how it would work, or more appropriately under which circumstances it would work. For web services where the CSR isn't critical (e.g. things not IIS) and services that can be restarted from the command line, though, Certbot on Windows seems a viable option to automate the process. Definitely worth giving it a shot!