Let's Encrypt on IIS

21 September 2016

I had heard about Let's Encrypt quite some time ago but never got around looking at it.

It seems to be a mostly unixy project and earlier on there were no implementations for Windows Server. In February 2016 Rick Strahl wrote a good introduction for IIS users and talked about the different options. Being a PowerShell guy I opted for AcmeSharp which is what the other two options use under the hood anyway.

I followed the Quick Start for one certificate but then began to write my own script to automate the various steps. When halfway done I discovered that this work was already done by Bill Seddon who wrote the script Update-Certificate-http.ps1

Now updating a certificate can be done with a single line:

Update-Certificate-Http -alias "myalias" -domain "www.mydomain.com" -websiteName "Default Web Site" -checkParameters

From the trenches

While using AcmeSharp I ran into a few small problems, here's how I fixed them.

ExtensionLess static files

When using the http-01 challenge a file without an extension is written to \.well-known\acme-challenge', by default IIS doesn't server extensionless files. To fix this add a newweb.config` in that directory with the following content.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <staticContent>
            <mimeMap fileExtension="." mimeType="text/plain"></mimeMap>
        </staticContent>
    </system.webServer>
</configuration>

I tried to put this into my main web.config file but having the path attribute of the location node starting with a dot, killed the whole site.

https only sites

I have a site that only has https bindings, no port 80. The http-01 challenge is always using http://.../.well-known/acme-challenge, so it could not access the file. I tried to temporarily add an http binding but I also have strict transport security set for the site, so allowing http would require lots of changes to the site.

The way I worked around this is by using my honeypot 'catch all' site which handles all requests not handled by any other site on the server. Here I allow http. I added a virtual directory pointing to the correct location under the site in question.

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/site[@name='Honeypot']/application[@path='/']" -name "." -value @{path='/.well-known';physicalPath='%WebSitesDrive%\SiteName\FileRoot.well-known'

Using New-WebVirtualDirectory doesn't work because of the dot in the name.

After the challenge had worked, we can remove it:

Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST'  -filter "system.applicationHost/sites/site[@name='Honeypot']/application[@path='/']" -name "." -AtElement @{path='/.well-known'}

If you don't have a Honeypot site, just create a site with an http binding for the site in question and the correct virtual directory. Keep the site stopped except when updating your certificates.

Updating all certificates which are about to expire

I wrote a small script that loops through all SSL bindings in IIS and finds those Let's Encrypt certificates that expire in the next x days. It updates those using the Update-Certificate-Http.ps1 script.

This script, Update-LECertificate.ps1 is on GitHub

Scheduling the script

All that is left to do is scheduling the script to run once a week or so.

Luckily the script knows how to do this, just use:

Update-LECertificate.ps1 -schedule

This runs the script every Sunday at 3:30 am, for more options on how to schedule the script use:

Show-Help .\Update-LECertificate.ps1 -full

Tags: IIS | Security | Web

Pages in this section

Categories

ASP.Net | Community | Development | IIS | IT Pro | Security | SQL (Server) | Tools | Web | Work on the road | Windows