Let’s EncryptでSANs対応のドメイン認証をとる

2016年10月1日

このサイトはFujiSSLでドメイン認証をとりましたが、費用を掛けないのであればLet’s Encryptを利用する方法もあります。というかサーバーに同居している他のサイトはLet’s Encryptで認証していました。(今はFujiSSLに切り替わっています)

参考にしたのはLet’s EncryptのSSL証明書で、安全なウェブサイトを公開|さくらのナレッジ



certbotをインストールしてSSL証明書を発行

FreeBSD用にはpkgでバイナリパッケージが用意されているので、nginx用のプラグインと合わせてインストールします。

# pkg install py27-certbot security/py-certbot-nginx

そしてCentOS7の場合にはEPELリポジトリから。こちらはApacheプラググインをインストール。

# yum install epel-release
# yum install certbot python-certbot-apache

実行ファイルはcertbot。certonlyコマンドを使って、-wに続けて対象ディレクトリと-dに続けてドメイン名を指定します。メールアドレスを訊かれたあとライセンス内容を承諾すると、SSL証明書が発行されます。

# certbot certonly --webroot -w /usr/local/www/sample/ -d www.example.com -d example.com

ドメイン名は複数回指定することでSANs対応の証明書を発行することができます。無事SSL証明書が発行されると、FreeBSDであれば/usr/local/etc/letsencrypt/live/www.example.com/の下に、CentOSであれば/etc/letsencrypt/live/www.example.com/の下に証明書ファイルが生成されます。

nginxの設定

443番のhttpsポートに対してssl_certificateとssl_certificate_keyをそれぞれ設定します。それからhttp接続されたリクエストをhttpsにリダイレクトさせるため、80番のhttpポートに対してreturn 301を設定します。

server {
  listen       80;
  server_name  .example.com;
  return       301 https://www.example.com$request_uri;
}
server {
  listen       443 ssl;
  server_name  example.com;
  ssl_certificate        /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
  ssl_certificate_key    /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;
  return       301 https://www.example.com$request_uri;
}
server {
  listen       443 ssl;
  server_name  www.example.com;
  ssl_certificate        /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
  ssl_certificate_key    /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;
  location / {
    root   /usr/local/www/sample;
    index  index.html index.htm;
  }
}

Apacheの設定

Apacheのsslモジュールは別途mod_sslをインストールしなくてはいけないのですが、yumでcertbotをインストールすると依存パッケージとして自動的に追加されているはずです。そしてsslの基本設定が/etc/httpd/conf.d/ssl.confとして用意されています。

ここではバーチャルホスト毎に/etc/httpd/conf.dディレクトリ内の設定をしてみます。

Apacheに組み込まれているrewriteモジュールを使って、httpアクセスをhttpsにリダイレクトするとともに、httpsアクセスのwww有無をすべてwww有りにリダイレクトしています。

<VirtualHost *:80>
  ServerName example.com
  ServerAlias www.example.com
  RewriteEngine On
  RewriteCond %{HTTP_HOST} example\.com
  RewriteRule ^(.*)$ https://www.example.com/%{REQQUEST_URI} [R,L]
</VirtualHost>
<VirtualHost *:443>
  DocumentRoot /var/www/html/example
  ServerName example.com
  ServerAlias www.example.com
  <IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
  </IfModule>
  RewriteEngine On
  RewriteCond %{HTTP_HOST} ^example\.com
  RewriteRule ^(.*)$ https://www.example.com/%{REQQUEST_URI} [R,L]
  TransferLog logs/ssl_access_log
  ErrorLog logs/ssl_error_log
  LogLevel warn
  SSLEngine on
  SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
  SSLCertificateFile /etc/letsencrypt/live/www.example.com/cert.pem
  SSLCertificateChainFile /etc/letsencrypt/live/www.example.com/chain.pem
  BrowserMatch "MSIE [2-5]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
  CustomLog logs/ssl_request_log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\"%b"
</VirtualHost>

証明書の更新

最後にcrontabに証明書の更新設定を入れておきましょう。

0    0    25    *    *    /usr/bin/certbot renew

こんな感じで毎月決まった日に更新をかけてもいいのですが、もう少し頭いい感じで更新期限が10日を下回った時だけ更新するようなスクリプトを書いてみます。なおdateコマンドの–dateオプションはLinux版だけみたいなので、他のOSでは少し工夫が必要です。

#! /usr/bin/bash
cert_file="/etc/letsencrypt/live/www.example.com/cert.pem"
exp_date=`openssl x509 -text -noout -in $cert_file | grep -e 'Not After' | cut -d ':' -f 2-`
diff_date=$(((`date --date="$exp_date" +%s` - `date +%s`) / 86400))
if test $diff_date -lt 10; then
  /usr/bin/certbot renew
fi

これをcronで毎日実行すればOK。

@daily    /root/renew.sh