Tag Archives: Web Development

Centralized Let’s Encrypt Management

Updated March 16, 2017 to reflect current webroot settings

Recently I set out to see how I could manage lets encrypt certificates from one central server, even though the actual websites didn’t live on that server. My reasoning was basically “This is how I did it with SSLMate, so let’s keep doing it” but it should also be helpful in situations where you have a cluster of webservers, and probably some other situations that I can’t think of at this time.

Before I get too in depth with how this all works, I’m going to define what I mean by two servers we have to work with:

  • Cert Manager: This is the server that actually runs Let’s Encrypt, where we run commands to issue certificates.
  • Client Server: This is the server serving the website, say… chrismarslender.com ūüėČ

Additionally, I have a domain setup that I point to the Cert Manager. For the purposes of this article, lets just call it certmanager.mywebsite.com.

High Level Overview

At a high level, here’s how it works with the web root verification strategy:

  1. I set up nginx on the Cert Manager to listen for requests at certmanager.mywebsite.com, and if the request is for anything under the path /.well-known/ I serve up the file the request is asking for.
  2. On the client servers, I have a common nginx include that matches the /.well-known/ location, and proxies that request over to the certmanager.mywebsite.com server.

Nginx Configuration

Here’s what the configuration files look like, for both the Cert Manager Server as well as the common include for the client servers:

Cert Manager Nginx Conf:

server {
    listen 80;
    
    server_name certmanager.mywebsite.com;
    
    access_log /var/log/nginx/cert-manager.access.log;
    error_log /var/log/nginx/cert-manager.error.log;

    root /etc/letsencrypt/webroot;

    location /.well-known {
        try_files $uri $uri/ =404;
    }

    location / {
        return 403;
    }
}

Client Server Common Nginx Include:

location ~ /\.well-known {
    proxy_pass http://certmanager.mywebsite.com;
    resolver 8.8.8.8;
}

Issuing a Certificate

Now lets say I want to issue a certificate for chrismarslender.com – here is what the process would look like.
I’m assuming chrismarslender.com is already set up to serve the website on a client server by this point.

SSH to the Cert Manager server, and run the following command:

letsencrypt certonly -a webroot --webroot-path /etc/letsencrypt/webroot -d chrismarslender.com -d www.chrismarslender.com

Eventually, this command generates¬†a verification file in the /etc/letsencrypt/live/.well-known/ directory, and then Let’s Encrypt tries to load the file to verify domain ownership at chrismarslender.com/.well-known/<file>.

Since the client server hosting chrismarslender.com is set up to proxy requests under /.well-known/ to the Cert Manager server (using the common include above), the file that was just created on the Cert Manager server is transparently served to Let’s Encrypt, and ownership of the domain is verified. Now, I have some fancy new certificates sitting in /etc/letsencrypt/live/chrismarslender.com

At this point, you just have to move the certificates to the final web server, reload nginx, and you’re in business.

In practice, I actually use ansible to manage all of this – I’ll work on a follow up post explaining how that all works as well, but generally I end up issuing SSL certificates as part of the site provisioning process on the Client Servers, in combination with `delegate_to`. Also, ansible makes steps like the moving of certificates to the final web server must less labor intensive ūüôā

Things to Figure Out

I’m still trying to figure out the best strategy to keep the certificates updated. I can run the Let’s Encrypt updater on the Cert Manager server and get new certificates automatically, but since it’s not the web server that actually serves the websites, I need to figure out how I want to distribute new certificates to appropriate servers when they are updated. Feel free to comment if you have a brilliant idea ūüėČ

WordCamp Portland 2013

WordCamp Portland 2013 is fast approaching. It is Saturday August 10th, at the Eliot Center in Portland, and I’ll be there.¬†WordCamps are informal, community-organized events that are put together by WordPress users. Everyone from casual users to core developers participate, share ideas, and get to know each other. There should be a lot of interesting talks going on, including some by some of my new coworkers at 10up.

I’m really excited for a day full of WordPress oriented presentations and conversations, and hope to see some of you there!

PHP’s strlen() function sometimes produces unexpected results

As UTF-8 encoding becomes more common in web development, issues arise every now and again that cause bugs in code, and these bugs are not always easy to figure out. I was recently working on a Swedish website, Opus Bilprovning, through Graphic Fusion here in Tucson. Since the site is in Swedish, there are some extra characters that I had to deal with that I don’t use everyday, such as √§, √•, and √∂ to name a few. Other than trying to pick up a few basic Swedish words, these new characters didn’t seem to be much of a big deal.

Then strlen() came along and broke the script

All was going fantastic until I was trying to extract out a word from a longer string that was UTF-8 encoded. As a simplified example, lets take the word “tj√§nster”, which is Swedish for “services” and count the characters using strlen()


$string = "tjänster";
echo "Length: " . strlen($string);

Length: 9
Looking at the word, I count 8 characters, but strlen() is telling me there are 9 characters. Why is this so? The problem comes from how strlen() determines the number of characters in the string. In many character sets, one character is represented with 1 byte, so the length of the string is the same thing as the number of bytes in the string. Since this is the case, php’s strlen() function returns the number of bytes in the string.

Why this doesn’t work with UTF-8 strings

In UTF-8, not all characters are represented with 1 byte. In fact, characters can be represented with as many as 4 bytes in UTF-8. In this example, the character “√§” is represented using 2 bytes in UTF-8, so the strlen() function returns 9 rather than the expected 8.

How this is solved

If all you are trying to do is count the length of the string, the solution is very simple; just pass the string through utf8_decode() prior to calling the strlen() function.


$string = "tjänster";
echo "Length: " . strlen( utf8_decode($string) );

Length: 8

Alternate Solution

In addition, there is another function that can be used to accomplish the same thing, and it has the added benefit that it can be used with character sets other than UTF-8. The function is mb_strlen(), and it requires two pieces of information to be passed to it, first, the string to determine the length of, and second, the encoding that is used for the string.


$string = "tjänster";
echo "Length: " . mb_strlen( $string, 'utf-8' );

Length: 8