It’s been a while since my last post here. This is my first post with the new platform - Hexo. It’s faster and simpler than Octopress and it’s not Ruby but nevermind…

The whole concept with the Certification Authorities is completely broken but we don’t have something better which is working. A world with fully encrypted web is a really a good idea since the whole internet traffic is monitored by governments and other private organizations. Let’s encrypt is an attempt for that. It’s a colaborative project between Linux foundation, EFF and some other organizations.

They are providing free (completely free!) certificates with 3 months of validity. After that time, the certificates can be updated again.

Signing and delivery of the certificates

Let’s encrypt is using the ACME (Automated Certificate Management Environment) protocol which defines automatically obtaining of certificates. More information about the protocol can be found at the Let’s Encrypt - How it works page.

So, let’s start the technical part

In this setup I’ll use Ubuntu 14.04 with HAProxy for load balancing and managing the traffic for all of the domains.

Install HAProxy:

HAProxy will directly deliver bind the HTTPS content but we need SNI checks for the Acme client. So, it’s a bit bizzare. We will have a loop inside HAPproxy. A TCP proxy frontend which is proxying a backend from localhost to the HTTPS proxy frontend.

TCP frontend -> HTTPS backend/ACME client backend -> HTTPS frontend -> Application Servers HTTP backends

1
2
3
4
sudo apt-get install -y software-properties-common
sudo apt-add-repository ppa:vbernat/haproxy-1.5
sudo apt-get update
sudo apt-get install -y haproxy

Configure HAProxy:

Let’s start with the default global config:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
tune.bufsize 131072
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /srv/certs
crt-base /srv/certs
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL).
ssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:!RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
# tunning
maxconn 16384

Then, append the default configuration for the http/s frontends:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defaults http
log global
mode http
option httplog
option dontlognull
option forwardfor
option http-server-close
timeout connect 5s
timeout client 310s
timeout server 310s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http

Setup the HTTP frontend. It will only refer the http requests to https:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
11
frontend www-http
bind 0.0.0.0:80
reqadd X-Forwarded-Proto:\ http
option forwardfor
#ACLs
acl example_sites hdr(host) -i example.com www.example.com
#Redirects
redirect prefix https://cloud.grandcity-property.com if example_sites

Now, create the HTTPS frontend. The port must be different than 443.

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
frontend www-https
bind 0.0.0.0:4443 ssl crt example.com.pem crt www.example.com.pem crt ./ no-sslv3
reqadd X-Forwarded-Proto:\ https
option forwardfor
#ACLs
acl example_sites hdr(host) -i example.com www.example.com
use_backend examplecom if example_sites

Create the backend for example.com:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
backend examplecom
timeout server 30m
balance leastconn
option httpclose
option forwardfor
cookie JSESSIONID prefix
server node1 192.168.0.10:80 cookie A check

Now, the TCP Proxy part. TCP defaults:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
#### TCP Section
defaults tcp
log global
mode tcp
option tcplog
timeout connect 10s
timeout client 600s
timeout server 600s

The TCP frontend (listening on port 443:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
frontend www-https-tcp
log global
mode tcp
option tcplog
bind 0.0.0.0:443
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
# Matching all SNI names with *.acme.invalid
acl app_letsencrypt req.ssl_sni -m end .acme.invalid
use_backend letsencrypt if app_letsencrypt
# sending everything that doesn't match *.acme.invalid to the HTTPS backend
default_backend bk_frontend_https_loop

And the backends for www-https-tcp:

[/etc/haproxy/haproxy.cfg]
1
2
3
4
5
6
7
8
9
10
11
backend bk_frontend_https_loop
log global
mode tcp
option tcplog
server localserver 0.0.0.0:4443
backend letsencrypt
log global
mode tcp
option tcplog
server letsencrypt 0.0.0.0:63443 #By default, Let's encrypt works on 443.

The whole haproxy.cfg is in this gist.

Now, install the official Let’s encrypt client:

Install git:

1
sudo apt-get install -y git

Download the client:

1
git clone https://github.com/letsencrypt/letsencrypt /root/letsencrypt

And create the certificate for example.com and wwww.example.com:

1
/root/letsencrypt/letsencrypt-auto --email admin@example.com -d example.com -d www.example.com --authenticator standalone --tls-sni-01-port 63443 --text auth --http-01-port 8099

Now, download ssl-cert-check from Prefetch.net. This is very useful script because it calculates the time difference between the current time and the certification expiration date:

1
2
sudo wget -O http://prefetch.net/code/ssl-cert-check /usr/local/bin/ssl-cert-check
sudo chmod +x /usr/local/bin/ssl-cert-check

Put this script to /etc/cron.daily/updatessl and make it executable :

1
2
sudo wget -O https://gist.githubusercontent.com/eniac111/c7146b3e59c7eff27fbe/raw/506643d797811ec99ce6e32d8f9e23ea3a9200d4/updatessl.sh /etc/cron.daily/updatessl
sudo chmod +x /etc/cron.daily/updatessl

The last thing is to create logrotate config for the update log:

Install logrotate it it’s not installed:

1
sudo apt-get update && sudo apt-get install -y logrotate

Put this to /etc/logrotate/letsencrypt-update:

[/etc/logrotate/letsencrypt-update]
1
2
3
4
5
6
7
8
9
/var/log/letsencrypt-update.log {
monthly
rotate 12
compress
delaycompress
missingok
notifempty
create 644 root root
}

Correct me if there is a better way. The double loop inside HAProxy is really bizzare.