オープンソースこねこね

Webプログラミングなどについてあれこれ。

CentOSにLet's EncryptのSSL証明書を導入する

やってみたので、手順をまとめました。実施したのはCentOS6+Apache2.2という、やや古い環境ですがCentOS7とかでも基本的には同じはずです。

Let's Encryptの概要

Let's Encryptは無料のSSL証明書を発行する認証局。 特徴は料金無料であるのと、専用のクライアントコマンドを使って証明書の発行を行う点。またワイルドカードの証明書は発行できない。有効期限が3ヶ月で、更新もコマンドによって自動化するのが前提となっています。

証明書を発行する際の要件として、当然ドメイン所有者の認証(本当にそのドメインの所有者か確認を行うこと)が必要になります。通常の認証局はメールなどを使ってこれを行いますが、Let's EncryptはACMEプロトコルという通信プロトコルと専用のクライアントコマンドを使います。ACMEプロトコルについて以下の記事が参考になりました。

Let's Encrypt を支える ACME プロトコル - Block Rockin’ Codes

ざっくり理解したところでは、ACMEプロトコルの内容は難しいものではなく、HTTPをベースに使った証明書の自動発行手順といった感じのものです。

さて、実際にLet's Encryptの証明書を導入するには一般に以下の作業が必要です。

  • クライアントコマンドのインストール
  • HTTPサーバの用意
  • クライアントコマンドを実行して証明書を取得
  • 取得した証明書使うためのHTTPサーバの設定
  • 自動更新のための設定

以下、順に記述します。

手順

クライアントコマンドのインストール

ACMEプロトコルでLet's Encryptのサーバと通信して証明書発行を行うCLIツールです。ここでは公式が提供しているcertbot-autoというツールを使います。

https://certbot.eff.org/

シェルスクリプトPythonで構成されています。このツールの操作自体、基本的にroot特権が必要になるので、以下のインストール作業のコマンドも全てrootユーザで行いました。

# yum install -y epel-release
# curl https://dl.eff.org/certbot-auto -o /usr/bin/certbot-auto
# chmod a+x /usr/bin/certbot-auto
# certbot-auto --os-packages-only --non-interactive

最後のcertbot-auto --os-packages-only --non-interactiveで依存パッケージがインストールされます(pythonのランタイムとか)。

HTTPサーバの用意

証明書発行時にACMEプロトコルドメインの認証処理(acme challengeというらしい)において、証明書を発行するドメインに対してHTTPでリクエストを投げてきます。 よってHTTPサーバが稼働している必要があります。ここではapache使うことにします。

# yum install -y httpd

インストールしたらドメイン名でアクセスできるように設定しておきます。

クライアントコマンドを実行して証明書を取得

certbot-auto certonlyで証明書を取得します。証明書発行に必要な秘密鍵などもこのコマンドを叩くだけでまとめて生成されます。

# certbot-auto certonly --non-interactive --agree-tos --webroot -w /var/www/html -d example.com --email kohki.makimoto@gmail.com

以下のオプションは環境に合わせて読み替えてください。

  • -d: ドメイン名を指定します。

  • --email: 更新期限が近づいたときにメール通知されるメアドを指定します。

  • -w: HTTPサーバのドキュメントルートを指定します。

-wオプションについて補足します。前述したようにACMEプロトコルドメインの認証処理でHTTPサーバにアクセスしてくるのですが、その際/.well-known/acme-challenge/というパスに対してアクセスしてきます。クライアントコマンドはこのパスにLet's Encrypt が指定したテキストを配置する必要があるので、そこにアクセスできるようドキュメントルートを指定するわけです。

その他オプションについての詳細はcertbot-auto --help allで確認してください。

うまくいくと

/etc/letsencrypt/live/{ドメイン名}

というパスに証明書や秘密鍵が生成されます。

取得した証明書使うためのHTTPサーバの設定

最近はSNIを使ってバーチャルホストごとにSSL設定ができるので、SSL証明書の設定はバーチャルホストごとに設定しました。

<VirtualHost *:443>
    ServerName example.com
    # ...

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

</VirtualHost>

なお、サーバ全体のSSL設定は/etc/httpd/conf.d/ssl.confなどに以下のように記述しておきます。

LoadModule ssl_module modules/mod_ssl.so

AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl    .crl

SSLPassPhraseDialog  builtin
SSLSessionCache         shmcb:/var/cache/mod_ssl/scache(512000)
SSLSessionCacheTimeout  300
SSLMutex default
SSLRandomSeed startup file:/dev/urandom  256
SSLRandomSeed connect builtin
SSLCryptoDevice builtin
SSLStrictSNIVHostCheck off

Listen 443

また、必要に応じてhttpにアクセスがあったらhttpsにリダイレクトさせる設定もいれます。

<VirtualHost *:80>
    ServerName example.com
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
</VirtualHost>

自動更新のための設定

certbot-auto renewコマンドで証明書の更新ができます。Let's Encryptの証明書は3ヶ月で有効期限が切れるので、cronに以下のコマンドを仕込みます。この例は出力を捨ててますが、ちゃんと運用する場合はきちんとログに残しましょう。

50 3 * * * root certbot-auto renew --post-hook "/usr/sbin/apachectl graceful" > /dev/null 2>&1

証明書取得時のようにドキュメントルートの指定はしていないので、/etc/letsencrypt/配下の設定ファイルなどを見て更新しているっぽいです。

–-force-renewをつけて実行すると有効期限が1ヶ月未満にならなくても、強制的に有効期限を更新します。手動で更新検証するのに使いました。

以上で一通り、作業は完了です。

その他

Basic認証をかけたサイトにLet's Encryptを使う

Basic認証をかけているサイトだとACMEプロトコルドメイン認証で、HTTPサーバにアクセスできず、証明書取得ができません。 以下の設定を入れて/.well-known/acme-challengeに穴をあけておくとうまくいきます。

<Location />
    Satisfy Any
    AuthType Basic
    AuthName "My Private Stuff"
    AuthUserFile /var/www/example/.htpasswd
    Require valid-user

    SetEnvIf Request_URI "/.well-known/acme-challenge" acme-challenge
    Order Deny,Allow
    Deny from all
    Allow from env=acme-challenge
</Location>

サードパーティツール

クライアントツールはサードパーティーのGo実装のシングルバイナリもあるのですが、現状見送り。公式のほうがやはり安心感ある。

CentOS6のpythonのバージョンが古い

CentOS6はpythonのバージョンが古いのでcertbot-auto実行時に以下のような警告がでました。が、正常に証明書取得までできたのでまあいいかな、と。。。

/root/.local/share/letsencrypt/lib/python2.6/site-packages/cryptography/__init__.py:26: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6
  DeprecationWarning

いろいろ参考にさせていただいたサイトなど

ありがとうございました。