Aymeric on Software

Because we needed another of these blogs...

The .NET Core Zoo

Unless you have been living under a rock, you may have noticed that Microsoft has been iterating at a furious pace on their .NET Core initiative for the past two years.

It is a clean reboot of the .NET Framework that is both open-source and cross-platform, targeting Windows, macOS and Linux.

For a company that has historically been an enemy of open-source software, it is difficult not to be amazed at the amount of openness that Microsoft is demonstrating with .NET Core. Not only have they released the entire stack as open source software but the development is also being done in the open on Github.

The issue with .NET Core is that it can easily be confused with older closed source products that Microsoft has released for the past 15 years. The problem is compounded by Microsoft’s willingness to re-use similar names to designate radically different underlying technologies.

In this post, I will provide you with an overview of .NET Core, ASP.NET Core and Visual Studio, and I will explain how the open-source flavors differ from their closed source relatives.

The Different Flavors of .NET Framework

Different flavors of the .NET framework have coexisted for some time. Typically each flavor combines a different runtime and a different set of standard libraries.

  • The .NET framework denotes the classical Windows-only .NET framework. This is the oldest flavor of .NET which shipped with Windows XP.

  • The .NET Core framework is the new open-source framework released in 2016. It works on Linux, macOS and Windows. Generally speaking, it can be considered as a subset of the older .NET Framework.

  • Xamarin Mono is a third party framework developed by open source Linux developers. It predates .NET Core by more than 10 years. It is also considered a subset of .NET Framework, because the Xamarin developers have never managed to quite catch up with Microsoft. However Mono also features special support for iOS, Android, Game Consoles, and Linux UI frameworks, which is entirely absent from Microsoft’s flavors.

  • .NET Standard is a recent initiative from Microsoft which formalizes the APIs shared by the different flavors of .NET. If your code targets .NET Standard, it should be able to run without recompiling on all flavors of .NET runtimes: .NET, .NET Core and Xamarin Mono.

The Different Versions of .NET Core

Microsoft’s download page for .NET Core shows many different options:

  • Microsoft offers both a Long Time Support (LTS) version and a regular version. The LTS version is older but Microsoft promises to support it for about 3 years. The downside is that it lags behind in terms of features.

  • A runtime and SDK versions. The SDK includes the runtime necessary for running .NET Core executables, but also all the development tools (i.e. the compiler and dotnet command line tool). Thus you only need to install one of them.

  • The Windows Server Hosting includes the runtime, but also a special module necessary to run ASP.NET Core sites within IIS. Since IIS is only available on Windows the Windows Server Hosting is not available on Linux nor macOS.

The Different Versions of Visual Studio

You can develop for .NET Core by using the text editor of your choice and the command line dotnet tool. However, I would highly recommend that you have a look at the different versions of Visual Studio that Microsoft provides.

  • Visual Studio 2017 for Windows. This is the traditional version of Visual Studio. The Community edition is free and fully featured when it comes to .NET and .NET Core development. The only downside is that you will probably have to use the paid-for Professional or Enterprise editions in a commercial setting.

  • Visual Studio Code is an open-source text editor based on Atom. It is a very capable text editor which also provides advanced IDE features, such as refactoring tools, compiler and debugger support for common languages including C#. It competes against Atom and Sublime Text.

  • Visual Studio 2017 for Mac. Although it shares the same name as the Windows version, it is a radically different piece of software based on Mono Develop. It is a bit rough and has definitely not achieved the level of maturity as the Windows counterpart. That being said, Visual Studio Code was in a similar state when it was introduced in 2015, and I am confident that Microsoft will iterate and make Visual Studio for Mac a lot better soon.

The Different Versions of ASP.NET

This the area where the marketing at Microsoft has gone wrong. The name ASP.NET Core refers to old technologies which gives this very modern and compelling framework a very bad press, in my opinion.

  • ASP, Active Server Pages is a very old technology released in the late 1990s. Think of it as a clone of PHP but using Visual Basic instead. It is also similar to what JSP for Java used to be. It was abandoned long ago and is not suitable for modern web application development.

  • ASP.NET superceded ASP in the early 2000s, and development shifted from Visual Basic to C#. It is a framework based on Web Forms, with a page template system powered by XML and uses the *.aspx file extension. The framework was invented before AJAX became mainstream, and a lot of effort seems to be devoted to make sure developers never write any JavaScript. It also integrates with the System.Web pipeline of IIS which, even though is a step up from CGI, makes use of global variables to represent the state of requests and responses. Even though it is still maintained, it is a technology that is clearly showing its age.

  • ASP.NET MVC is a modern framework that Microsoft introduced in 2009. It is inspired by the successful Ruby on Rails framework, and as the name indicates, features a clear separation between Models, Views and Controllers. It also introduced a better view template system named Razor and improved routing. Even though it is still integrated with the same System.Web IIS centric pipeline, it goes out of its way to hide that fact from developers, and wrap HTTP requests and responses into non-global variables, which can be mocked for the purpose of unit testing.

  • ASP.NET Web API is another framework Microsoft introduced in 2012. It is very similar to ASP.NET MVC, to the point that entire class hierarchies between the two frameworks share identical names and functions. But unlike ASP.NET MVC which focuses on generating dynamic HTML pages, ASP.NET Web API as the name indicates, targets the creation of Web APIs. It is a pretty good framework to write JSON RESTful APIs for instance.

  • Finally ASP.NET Core is a complete rewrite and a merge of both ASP.NET MVC and ASP.NET Web API. It was released in 2016 and is developed in the open on Github. It makes great use of dependency injection and provides support for writing middleware. ASP.NET Core web applications can be deployed to Linux servers or Docker containers.

The Different Versions of ASP.NET Core

As I indicated in the previous section, ASP.NET Core is a complete reboot of the Microsoft framework for writing web application software.

But the moniker ASP.NET Core denotes some radically different realities:

  • The version of ASP.NET Core which targets Kestrel. This is the main version that Microsoft is promoting. Kestrel is an open-source asynchronous HTTP server with very good performance characteristic. If you target .NET Core as your runtime, this is the only available option. Kestrel and .NET Core can be deployed to Linux (possibly using nginx or Apache as a reverse proxy). You can also target .NET Standard or .NET 4.7 for Windows. In this flavor of ASP.NET Core, the entire HTTP pipeline has been rewritten from scratch, and you can inject your own middleware, as in any modern web application framework.

  • The version of ASP.NET Core which integrates with the IIS pipeline. This flavor of ASP.NET Core integrates with the classic IIS pipeline introduced for ASP.NET and it relies on the System.Web classes and their global variables. If you adopt this flavor of ASP.NET Core you have to target .NET 4.7 and run the web application on IIS on a Windows Server. In addition some features, such as writing your own custom middleware, are not available. The advantage of this flavor of ASP.NET Core, is that it can be viewed as an incremental update from the old ASP.NET MVC and ASP.NET Web API frameworks. Unless you have some existing ASP.NET code, you should probably stay away from this configuration for new projects.

  • Finally you can use ASP.NET Core with Kestrel inside IIS. So this is the option where you are essentially using IIS as a reverse proxy for the Kestrel server. In this version you sort of get the best of both worlds: your Windows centric IT team can continue managing IIS sites as before, and you get all the features and performance improvements available to the Kestrel stack. This is achieved by installing a custom IIS module provided by Microsoft, which is called the ASP.NET Core Module. This is the recommended way to deploy ASP.NET Core on Windows servers. On Linux Microsoft recommends using nginx or Apache in similar reverse proxy configurations.

Conclusion

.NET Core and ASP.NET Core are very compelling open source technologies promoted by Microsoft. The frameworks, libraries, compilers and runtime are published on Github. You can use Visual Studio Code, another open-source piece of software to write C# code and deploy it to Linux, possibly via docker containers.

But make no mistake, Microsoft still has ways to make money:

  • Microsoft sells access to Azure, their own cloud computing platform which competes with the likes of AWS and Google Cloud. Naturally Azure integrates very well with ASP.NET Core.

  • Microsoft also sells Visual Studio 2017, which unlike Visual Studio Code is closed source software. Although it may be used for free under some circumstances, for most commercial settings you will have to pay a subscription to use it.

  • Microsoft is also strongly pushing SQL Server, their closed source relational database server. It has better integration than any other SQL databases with the .NET Core stack. Traditionally SQL Server used to run on Windows Servers only, but since the last release of SQL Server 2017 last October, the software can now be deployed to Linux servers as well.

Let’s Encrypt With Nginx

The Objectives

One of the major hurdles hampering the deployment of HTTPS on smaller websites like this one, has always been the price of certificates. As much I would have liked to get one, I could hardly justify the cost. That’s why a year ago, when the Let’s Encrypt project was announced, with the promise of free domain certificates, I was particularly excited. I decided to migrate my websites as soon as the project reached the public beta phase, last week.

Even if your site does not require HTTPS for security reasons, it is worth considering:

  • to provide additional privacy to your users. Unscrupulous ISPs have started to inject javascript or cookies into third party HTTP pages. They cannot do this with HTTPS pages.

  • to benefit from the performance improvements of HTTP/2. Even though in theory HTTP/2 does not require TLS, all major browsers have decided to boycott the “plaintext” version of the protocol.

In this blog post, I intend to explain how I migrated this Octopress blog hosted with nginx from HTTP to HTTPS and obtained an A+ Grade from SSL Labs.

Intall Let’s Encrypt

This part of the process is very well covered by the Let’s Encrypt documentation. Since there are no packages for Ubuntu Server LTS at the moment, I used the source code approach. In this case, we end up using the letsencrypt-auto command instead of letsencrypt directly. letsencrypt-auto is a wrapper script that ensures that the tool and its dependencies are up to date, prior to running any letsencrypt commands.

1
2
3
4
5
6
7
8
9
# Optionally install git
sudo apt-get install git-core

# Checkout the let's encrypt git repository into /srv/letsencrypt
git clone https://github.com/letsencrypt/letsencrypt /srv/letsencrypt

# Run the tool at least once
cd /srv/letsencrypt
./letsencrypt-auto --help

When I did this, I happened to have some kind of InsecurePlatform Python warning.

1
2
3
4
5
Creating virtual environment...
Updating letsencrypt and virtual environment dependencies...../home/aymericb/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
./home/aymericb/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning

This warning comes from urllib3. I ignored it and everything was fine!

Getting the Certificate

At this stage you are ready to request a certificate for the website. Let’s Encrypt uses the ACME protocol, which requires serving a bespoke generated file on the web server to confirm ownership of a domain. The letsencrypt tool aims to automate the whole setup, so that writing ./letsencrypt-auto --apache -d blog.barthe.ph should be sufficient to get the entire web server up and running, if you happen to use Apache.

Unfortunately, the plugin for nginx is not stable yet and cannot be used. The setup will be slightly more complicated. We will use the --webroot switch, telling letsencrypt where to find our web server files, and afterwards, we will have to modify the nginx configuration files ourselves.

Getting the Let’s Encrypt certificate is the easy part:

1
./letsencrypt-auto certonly --webroot -w /srv/blog.barthe.ph/www -d blog.barthe.ph

You are asked to provide an email address (used to warn you when your certificate is about to expire), and agree to the terms and conditions.

Afterwards, you need to manually edit the nginx configuration files using your favorite editor (e.g. nano -w /etc/nginx/sites-available/blog.barthe.ph). The TLS certificate and the private keys are located in /etc/letsencrypt/live/blog.barthe.ph.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Add the following to redirect HTTP requests to HTTPS
server {
        server_name blog.barthe.ph;
        listen 80;
        return 301 https://$server_name$request_uri;
}
# Modify the existing HTTP setup to use HTTPS
server {
        server_name blog.barthe.ph;
        listen 443 ssl;

        # Basic TLS setup
        ssl_certificate /etc/letsencrypt/live/blog.barthe.ph/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/blog.barthe.ph/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

        # keep the rest of the site configuration unchanged
        # [...]
 }

You’ll notice that in the configuration, I chose to support TLS 1.0 and greater. I wanted to be more aggressive but TLS 1.1 is not that well supported. It does not work on OS X 10.8 for instance. However, I did drop support for the older SSLv3 protocol (which preceded TLS 1.0) and is still supported by most websites. The consequence is that my site is no longer compatible with older versions of Internet Explorer (IE6 to IE8). Supporting SSLv3 without weakening the security of modern TLS 1.2 browsers is becoming increasingly difficult because of downgrade attacks such as POODLE.

In order for the changes to take effect you should invoke sudo service nginx reload. If you botched the configuration file, the site will continue running with the existing configuration and you should get an error in /var/log/nginx/error.log.

Once that’s done, you should have a web server running with HTTPS, and that redirects older HTTP URLs to their HTTPS equivalents. W00t!

A+ Grade Security

When testing the Octopress server on different browsers, I got some warnings about mixed content. It means that although my website was configured to serve its content through HTTPS, it still referenced URLs that used HTTP, either for internal URLs or externals URLs (such as Google Fonts, etc…). Chrome in particular was the most strict of all the browsers about this. After spending some time searching and replacing all references to HTTP URLs, I managed to fix all the warnings.

The next step was to edit /etc/nginx/sites-available/blog.barthe.ph to improve performance, by enabling TLS session caching:

1
2
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 20m;

Let’s now focus on improving security and get an A+ grade on SSL Labs.

Let’s start by overriding the default prime used for Diffie Hellman. The default prime is usually too small, and also since it is a shared prime, some pre-computed attacks like Logjam are possible. Beware, the openssl command will take a long time; from 20 minutes to a few hours.

1
2
3
4
# Be patient…
openssl dhparam -out /etc/ssl/private/dhparams_4096.pem 4096

# Add "ssl_dhparam /etc/ssl/private/dhparams_4096.pem;" to nginx config

Now, let’s add support for OCSP stapling. OCSP is a mechanism that can be used by the browser to confirm that a certificate has not been revoked. This normally involves an extra connection to the certificate authority, unless the website uses stapling.

1
2
3
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/blog.barthe.ph/fullchain.pem;

Let’s enable HSTS. This prevents a browser that connected to the website at least once before, from ever accepting an HTTP connection from the same domain.

1
add_header Strict-Transport-Security max-age=31536000;  # Valid for 1 year

Finally let’s massage the cipher suite to only use safe and secure ciphers.

1
2
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:... SEE BELOW';

In order to obtain the list of ciphers separated by :, use openssl ciphers -V to dump all available ciphers. Then do as follow:

  • Delete all references to DES, 3DES and RC4, and just keep AES ciphers.

  • Delete all references to MD5.

  • Put all ciphers that use SHA1, whose name ends with _SHA and are SSLv3 at the bottom.

  • Put ciphers with larger key lengths on top.

  • Put ciphers with forward secrecy on top. That would be ECDHE or DHE, the DH stands for Diffie-Hellman, and the E for Ephemeral, which means a new key is negotiated for each connection. Stealing the private key will not allow an attacked to decrypt past TLS sessions.

  • Favor GCM over CBC as a mode of operation. This is faster, and CBC brought security issues like POODLE in the past.

  • Favor Elleptic Curves (anything with EC) over plain RSA, DSA, or DHE. Elliptic curves are faster because they require smaller primes than traditional crypto, and as far as we know the security behind the maths is solid.

Here’s my list:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
0xC0,0x28 - ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
0x00,0xA3 - DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=DSS  Enc=AESGCM(256) Mac=AEAD
0x00,0x9F - DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
0x00,0x6B - DHE-RSA-AES256-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA256
0x00,0x6A - DHE-DSS-AES256-SHA256   TLSv1.2 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA256
0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128) Mac=AEAD
0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA256
0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA256
0x00,0xA2 - DHE-DSS-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=DSS  Enc=AESGCM(128) Mac=AEAD
0x00,0x9E - DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(128) Mac=AEAD
0x00,0x67 - DHE-RSA-AES128-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA256
0x00,0x40 - DHE-DSS-AES128-SHA256   TLSv1.2 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA256          
0xC0,0x2E - ECDH-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
0xC0,0x32 - ECDH-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
0xC0,0x26 - ECDH-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256)  Mac=SHA384
0xC0,0x2A - ECDH-RSA-AES256-SHA384  TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(256)  Mac=SHA384
0xC0,0x31 - ECDH-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(128) Mac=AEAD
0xC0,0x2D - ECDH-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(128) Mac=AEAD
0xC0,0x29 - ECDH-RSA-AES128-SHA256  TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(128)  Mac=SHA256
0xC0,0x25 - ECDH-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128)  Mac=SHA256
0x00,0x3D - AES256-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA256
0x00,0x9C - AES128-GCM-SHA256       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(128) Mac=AEAD
0x00,0x3C - AES128-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA256
0xC0,0x0A - ECDHE-ECDSA-AES256-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
0xC0,0x14 - ECDHE-RSA-AES256-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
0xC0,0x09 - ECDHE-ECDSA-AES128-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA
0xC0,0x13 - ECDHE-RSA-AES128-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA1
0x00,0x39 - DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
0x00,0x38 - DHE-DSS-AES256-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA1          
0x00,0x33 - DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
0x00,0x32 - DHE-DSS-AES128-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA1          
0xC0,0x22 - SRP-DSS-AES-256-CBC-SHA SSLv3 Kx=SRP      Au=DSS  Enc=AES(256)  Mac=SHA1
0xC0,0x21 - SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP      Au=RSA  Enc=AES(256)  Mac=SHA1
0xC0,0x20 - SRP-AES-256-CBC-SHA     SSLv3 Kx=SRP      Au=SRP  Enc=AES(256)  Mac=SHA1
0xC0,0x1F - SRP-DSS-AES-128-CBC-SHA SSLv3 Kx=SRP      Au=DSS  Enc=AES(128)  Mac=SHA1
0xC0,0x1E - SRP-RSA-AES-128-CBC-SHA SSLv3 Kx=SRP      Au=RSA  Enc=AES(128)  Mac=SHA1          
0xC0,0x1D - SRP-AES-128-CBC-SHA     SSLv3 Kx=SRP      Au=SRP  Enc=AES(128)  Mac=SHA1
0xC0,0x05 - ECDH-ECDSA-AES256-SHA   SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256)  Mac=SHA1
0xC0,0x0F - ECDH-RSA-AES256-SHA     SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(256)  Mac=SHA1
0xC0,0x0E - ECDH-RSA-AES128-SHA     SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(128)  Mac=SHA1
0xC0,0x04 - ECDH-ECDSA-AES128-SHA   SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128)  Mac=SHA1
0x00,0x35 - AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
0x00,0x2F - AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1	

Automatic Updates of the Certificate

As a matter of policy the certificates emitted by Let’s Encrypt are only valid for a short period of 90 days. This explains why the project focuses so much on automation.

It is possible to renew the certificate by executing the following commands, which I added to a /srv/letsencrypt/cron-update.sh script

1
2
3
#!/bin/sh
/srv/letsencrypt/letsencrypt-auto certonly --webroot -w /srv/blog.barthe.ph/www -d blog.barthe.ph --renew-by-default  --agree-tos
service nginx reload

Unfortunately, you’ll find out that the letsencrypt-auto command fails with the following error:

1
Failed authorization procedure. blog.barthe.ph (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Error parsing key authorization file: Invalid key authorization: 231 parts

I found out that the ACME protocol attempts to validate the ownership using HTTP and is unable to follow the HTTP to HTTPS 301 redirection we set up at the beginning. So I had to slightly change my setup in /etc/nginx/sites-available/blog.barthe.ph.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# The following lines prevent Let's Encrypt ACME protocol from working 
#server {
#        server_name blog.barthe.ph;
#        listen 80;
#        return 301 https://$server_name$request_uri;
#}
#
# Replace by these lines, to continue serving /.well-known/ files on port 80
server {
        server_name blog.barthe.ph;
        listen 80;
        location /.well-known/ {
                root /srv/blog.barthe.ph/www;
                try_files $uri $uri/ =404;
        }
        location / {
                return 301 https://$server_name$request_uri;
        }
}

The new version let all ACME requests to /.well-known/ be served normally over HTTP by nginx, but redirects all the others to HTTPS. Once that change is done and nginx reloaded, executing the letsencrypt-auto successfully updates the certificate.

The next is to add a CRON job, by typing crontab -e (as root). I set up mine to update the certificate once a month, as follow:

1
2
3
# contrab -e
# m h  dom mon dow   command
0 11 7 * * /srv/letsencrypt/cron-update.sh

Conclusion

So far I’m relatively happy with Let’s Encrypt. I have chosen to run the letsencrypt-auto run with root privileges, but if that bothers you, there is a project named Let’s Encrypt no sudo which aims to prevent that.

For websites with more traffic, you could delay the HTTP to HTTPS redirection until you have fully tested the HTTPS version of the site. This is a good opportunity to fix all mixed URL scheme content issues.

Finally, I published my final /etc/nginx/sites-available/blog.barthe.ph config file as a Gist.

Edit. I initially made a mistake and wrote return 301 https://blog.barthe.ph instead of return 301 https://$server_name$request_uri in the nginx configuration. The consequence is that URLs starting with http would be redirected to the home page of the blog, instead of the https counterpart page. This defect was somehow masked by HSTS, because after browsing once to the site, the browser would redirect to the correct pages.