OXY NOTES

WordPressのサイトをHTTPS化して学ぶLet’s Encryptの使い方

無料でHTTPS化できるLet’s Encryptの使い方を1から解説します

Let’s Encrypt(レッツ・エンクリプト)とはTLSやHTTPSによるセキュリティに配慮した通信を広めることを目的としたプロジェクトです。
プロジェクトが正式に開始したのは2016年4月と、まだ新しい試みながら、モジラ財団など大手がスポンサーに付いています。
日本でもサーバホスティングの大手である「さくらインターネット」や「GMO」でも無料SSL化の際に利用したり、ブックマークサービスで有名な「はてな」が今年の6月4日に支援を表明するなど、広がりをみせています。

Let’s Encryptの特徴はなんと言っても無料で暗号化通信に必要な証明書を利用できることです。
認証局で有名なシマンテックやコモドといった会社では1年間で数万~数十万円かかります。

無料だからといって、Let’s Encryptで発行される証明書と、シマンテック等の有料の認証局で発行される証明書にセキュリティ上の違いはありません
どちらも有効な証明書として機能します

Let’s Encryptは無料で誰でも利用できます。それは裏を返せば悪意あるサイトも利用できるということです。そのため、信頼性の担保という意味ではあまり役に立ちません
銀行口座やクレジットカードの情報を扱うなど、高度なセキュリティが必要なサイトの場合は、今まで通り有料の証明書を利用する必要があります。

また、Let’s Encryptの証明書は有効期限が3ヶ月と短いのも特徴です。(ただし後述する自動化を行えば半永久的に自動で更新することが可能)

ということで、Let’s Encryptによるhttps化についてまとめると以下のようになります。


Let’s Encryptを導入して証明書を発行する方法

Let’s Encryptには「Certbot クライアント」というコマンドがあります。
Certbot クライアント」を利用すれば証明書の発行から、自動更新まで細かく制御することができます。
今回はCentOS 6に導入する方法を解説します。

ディストリビューションによって細かな違いはあるものの、Let’s Encrypt公式の解説で詳しく説明しているので迷うことは少ないと思います。

EPELリポジトリの導入

# yum install epel-release

EPELリポジトリは、すでに導入しているという方も多いと思います。
ただしprioritiesで優先順位をつけている場合は注意が必要です。Certbotの導入にはEPELリポジトリの設定が有効である必要があります。「--enablerepo=epel」といったオプションを付けても失敗します。
無効にしている場合は以下のように有効にしておいてください。

# vi /etc/yum.repos.d/epel.repo

[epel]セクションのenabled=01へ変更。

Certbotクライアントの導入

EPELリポジトリを導入して有効にしたらCertbotクライアントをインストールします。
後で管理が面倒になるので、root権限でhomeディレクトリで実行してください。

# su -
# wget https://dl.eff.org/certbot-auto

実行権限を付与。以下のように表示されればOKです。

# chmod a+x certbot-auto
# ls -al
-rwxr-xr-x  1 root root       62854 May  3 04:25 certbot-auto

導入が完了したら「certbot-auto」を実行。
この方法で導入された「Certbotクライアント」はcertbotではなくcertbot-autoと入力します。公式の解説も適宜読み替えてください。

# ./certbot-auto

(省略)

Dependencies Resolved

================================================================================
 Package                Arch       Version                    Repository   Size
================================================================================
Installing:
 augeas-libs            x86_64     1.0.0-10.el6               base        314 k
 libffi-devel           x86_64     3.0.5-3.2.el6              base         18 k
 python34               x86_64     3.4.5-4.el6                epel         50 k
 python34-devel         x86_64     3.4.5-4.el6                epel        186 k
 python34-tools         x86_64     3.4.5-4.el6                epel        425 k
Updating:
 ca-certificates        noarch     2017.2.14-65.0.1.el6_9     updates     1.3 M
 gcc                    x86_64     4.4.7-18.el6_9.2           updates      10 M
 redhat-rpm-config      noarch     9.0.3-51.el6.centos        base         60 k
Installing for dependencies:
 python-rpm-macros      noarch     3-11.el6                   epel        5.4 k
 python-srpm-macros     noarch     3-11.el6                   epel        4.8 k
 python3-rpm-macros     noarch     3-11.el6                   epel        4.9 k
 python34-libs          x86_64     3.4.5-4.el6                epel        8.3 M
 python34-tkinter       x86_64     3.4.5-4.el6                epel        336 k
 tcl                    x86_64     1:8.5.7-6.el6              base        1.9 M
 tk                     x86_64     1:8.5.7-5.el6              base        1.4 M
Updating for dependencies:
 cpp                    x86_64     4.4.7-18.el6_9.2           updates     3.7 M
 gcc-c++                x86_64     4.4.7-18.el6_9.2           updates     4.7 M
 gcc-gfortran           x86_64     4.4.7-18.el6_9.2           updates     4.7 M
 libgcc                 x86_64     4.4.7-18.el6_9.2           updates     103 k
 libgfortran            x86_64     4.4.7-18.el6_9.2           updates     268 k
 libgomp                x86_64     4.4.7-18.el6_9.2           updates     134 k
 libstdc++              x86_64     4.4.7-18.el6_9.2           updates     296 k
 libstdc++-devel        x86_64     4.4.7-18.el6_9.2           updates     1.6 M

Transaction Summary
================================================================================
Install      12 Package(s)
Upgrade      11 Package(s)

Total download size: 40 M
Is this ok [y/N]: y ← Yes Noを聞かれるので、yでエンター。

yでエンターしてメールアドレスを聞かれるので登録。

Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): hoge@example.com ← メールアドレスを入力してエンター。

利用規約(pdf)を読んで同意するかどうかを聞かれるのでaを押してエンター。

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: a ← aを押してエンター。

電子フロンティア財団にメールアドレスを登録するかを聞かれるので好きな方を選んでエンター。

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: y ← yを押してエンター。

https化をするドメインを聞いてきます。そのままエンターを実行するとすべてのドメインで証明書の発行を行います。
ただし、この時点ですんなりと導入できることはまれなので、キャンセルのcを入力してエンターで良いと思います。

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: aqwiki.net
2: go-iken.net
3: oxynotes.com
4: sukegra.com
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): c ← cを押してエンター。

以下失敗した場合のサンプル

Waiting for verification...
Cleaning up challenges
Failed authorization procedure. go-iken.net (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://go-iken.net/.well-known/acme-challenge/hnajJtKsg0y6VlAguTH4vkVHvACiNXwXhtgzZd_tMzw: "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-style-type" content="", aqwiki.net (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://aqwiki.net/.well-known/acme-challenge/yXe0go9V81n1uUs_EQ-S43EPIzMq3Mo7-HC1ZWsWVRE: "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-style-type" content="tex"

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: go-iken.net
   Type:   unauthorized
   Detail: Invalid response from
   http://go-iken.net/.well-known/acme-challenge/hnajJtKsg0y6VlAguTH4vkVHvACiNXwXhtgzZd_tMzw:
   "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   <html>
   <head>
   <meta http-equiv="content-style-type" content=""

   Domain: aqwiki.net
   Type:   unauthorized
   Detail: Invalid response from
   http://aqwiki.net/.well-known/acme-challenge/yXe0go9V81n1uUs_EQ-S43EPIzMq3Mo7-HC1ZWsWVRE:
   "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   <html>
   <head>
   <meta http-equiv="content-style-type" content="tex"

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.

Certbotクライアントの使い方を解説

基本的なコマンドは公式のコマンド解説ページにまとまっています。

このページでは実際の証明書の発行と自動化の際に利用するコマンドに限定して解説します。

certonly

Webサーバを停止することなくSSL/TLSサーバ証明書を取得します
証明書のインストール(サーバソフトウェアの設定)は行われません。ここで言うサーバソフトウエアとはApacheNginxのことです。
installというサブコマンドを利用するか、より具体的に--apache--nginxといったプラグインオプションを指定すると自動でWebサーバの設定を更新することが可能です。

余談ですが、証明書の発行にはstandaloneというコマンドも用意されています。
standaloneは読んで字のごとく、Webサーバが動作していないサーバでもコマンド単体で証明書の発行が可能なコマンドです。
利用時にポート80と443を利用するため、ApacheやNginxでポートをバインドしている場合は利用できません。そのためWebサーバを導入しているサーバで実行する場合は、1度Webサーバを停止する必要があります。後述する自動更新とも相性が悪いため、Webサーバでhttps化を行う際は利用しません。

--webroot

このプラグインオプションでドメインに対応するドキュメントルートを指定します
Certbotクライアントでは、このオプションで指定したパスへ確認用のファイルを一時的に書き込むことで、サーバの所有権を確認します。

確認ファイルを書き込む場所の例

/var/www/html/example/.well-known/acme-challenge/test

具体的なディレクトリの指定は「-w」オプションの後に追記します。

-wの記述方法サンプル

--webroot -w /var/www/html/example/

続いて「-d」オプションでドメインを指定します。(--domainでもOK)

-dの記述方法サンプル

-d example.com

上記のオプションを組み合わせると以下のようなコマンドになります。

# ./certbot-auto certonly --webroot -w /var/www/html/example/ -d example.com

これでCertbotクライアントによって証明書が発行されます。
正しく証明書が発行されれば以下のようにログが流れます。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for example.com
Using the webroot path /var/www/html/example for all unmatched domains.
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2018-09-03. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

logにもある通り、証明書などのファイルは「/etc/letsencrypt/live/example.com/」に保存されています。
具体的にはNginxssl_certificateで使う証明書は「/etc/letsencrypt/live/example.com/fullchain.pem」。
キーファイルは「/etc/letsencrypt/live/example.com/privkey.pem」に保存されています。


証明書を自動で更新する方法

証明書が発行できたら自動更新を設定します。

Let’s Encryptの証明書は有効期限が3ヶ月と短いのが特徴です。
複数のサイトを運営している場合、何度も取得するのは手間です。そこで、cronを利用して自動で証明書を更新します

実はLet’s Encryptには自動更新用のコマンドが用意されています。
自動化の方法はさまざまありますが、このページではcrontabに新しいルールを追記する方法をとります。

# vi /etc/crontab

以下の行を追記してください。

0 5 * * * root /root/certbot-auto renew --post-hook "service nginx restart"

rootユーザー毎日5時に「./certbot-auto renew –post-hook “service nginx restart”」というコマンドを実行するという設定です。
crontabについて詳しく知りたい方は過去の投稿をご覧ください。

renew」というコマンドがメインの部分です。
renewコマンドを利用すると証明書の有効期限が30日以内の場合、新しい証明書を発行してくれます。

更新のタイミングは30日あるので、毎日ではなく1周間に1度程度でも良いと思います。その場合は以下のように曜日のフィールドを指定します。(0は日曜日)

0 5 * * 0 root /root/certbot-auto renew --post-hook "service nginx restart"

--post-hook」というオプションは、証明書を再発行した場合のみ、続くコマンドを実行します。
そのため、「service nginx restart」の部分は証明書が再発行されない限り実行されません。
具体的には「No hooks were run.」というログが流れ、処理が停止します。

ちなみに正しく更新されると以下のようなログが流れます。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/oxynotes.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for oxynotes.com
Waiting for verification...
Cleaning up challenges

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/oxynotes.com/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/oxynotes.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

更新がまだ必要ない場合は以下のログ

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/oxynotes.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

サーバ証明書の発行が失敗する場合の対処法

証明書の発行は様々な理由で失敗する場合があります。
原因と対処法を解説します。

失敗した場合のログ

   Domain: go-iken.net
   Type:   unauthorized
   Detail: Invalid response from
   http://go-iken.net/.well-known/acme-challenge/hnajJtKsg0y6VlAguTH4vkVHvACiNXwXhtgzZd_tMzw:
   "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   <html>
   <head>
   <meta http-equiv="content-style-type" content=""

   Domain: aqwiki.net
   Type:   unauthorized
   Detail: Invalid response from
   http://aqwiki.net/.well-known/acme-challenge/yXe0go9V81n1uUs_EQ-S43EPIzMq3Mo7-HC1ZWsWVRE:
   "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   <html>
   <head>
   <meta http-equiv="content-style-type" content="tex"

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.

このような失敗をする場合、以下の点を確認してみてください。

1.DNSでAレコードが正しく設定されているか。(ドメイン名を入力して正しく)

nslookupでAレコードとドメインの値が正しく反映されているか確認してみてください。

1.iptablesなど、ファイアーウォールでポート80と443が閉じていないか。

証明書の発行時にポート80と443を利用するので、ポートが空いているか確認してください。もしくはSSH用のポートを日本以外のIPからアクセス禁止等の処置をしていないか確認。

1.-wオプションで指定しているルートディレクトリにアクセスできるか。

.」で始まるファイルやフォルダへのアクセスを禁止していないか。
テストするには以下のコマンドを試してみてください。

-wオプションで指定するパスが「/var/www/html/example/」でサイトのドメインが「example.com」の場合

# mkdir -p /var/www/html/example/.well-known/acme-challenge/
# echo -n "Testing webroot acme challenge" > /var/www/html/example/.well-known/acme-challenge/test

コマンドを実行したら「http://example.com/.well-known/acme-challenge/test」へアクセス。
これで403エラー500エラーが出る場合はWebサーバの設定を見直す必要があります。

私の場合はWordPressの設定を行う際に読み込んでいた以下の設定が悪さをしていました。

include global/restrictions.conf;

具体的にはrestrictions.confにある以下の記述をコメントアウトすることで動作しました。

# vi /etc/nginx/global/restrictions.conf
location ~ /\. {
	deny all;
}

これは「.」で始まるファイルやフォルダへのアクセスを禁止する設定です。Apatchで利用される隠し設定ファイルへのアクセスを禁止するものです。Nginxの場合は通常利用していないため、コメントアウトしても構いません。

もしくは以下のように所有権の確認に使うディレクトリだけ有効にして、それ以外を無効にします。

location ~ /\.well-known/ {
	allow all;
}
location ~ /\. {
	deny all;
}

NginxでHTTPS化するために設定する

Nginxでドメインが「example.com」、ドキュメントルートが「/var/www/html/example」の場合のサンプルです。
serverディレクティブポート80をバインドしている場合です。

# vi /etc/nginx/nginx.conf

以下のような設定になっているものを

server{
	listen       80 default_server; # デフォルトサーバに指定
	server_name  example.com;

	root   /var/www/html/example; # ドキュメントルート
	index  index.html index.htm index.php; # indexの設定

	access_log  /var/log/nginx/example.com_access.log  main;
	error_log   /var/log/nginx/example.com_error.log warn;
(省略)

以下のように変更します。

server{
	listen       443 ssl default_server; # デフォルトサーバに指定
	server_name  example.com;

	ssl_certificate         /etc/letsencrypt/live/example.com/cert.pem;
	ssl_certificate_key     /etc/letsencrypt/live/example.com/privkey.pem;

	root   /var/www/html/example; # ドキュメントルート
	index  index.html index.htm index.php; # indexの設定

	access_log  /var/log/nginx/example.com_access.log  main;
	error_log   /var/log/nginx/example.com_error.log warn;
(省略)

listenポートを443、SSL通信を指定
ssl_certificatessl_certificate_keyで証明書とキーを指定します。ここのパスを変更するとcronで自動更新された証明書と整合性が取れなくなるので注意してください。

アクセスログなどは、分けなくても良いですが、切り替えたほうがベターです。

正しく設定できたらWebサーバを再起動してください。

# service nginx restart

WordPressの設定を変更する

まずは変更する前にテーマファイルとデータベースのバックアップを行ってください。

wp-config.phpの編集

WordPressのルートディレクトリにある「wp-config.php」を編集して、以下を追記します。

$_SERVER['HTTPS'] = 'on';
$_ENV['HTTPS'] = 'on';

この編集を加えないとログイン画面などでリダイレクトループになります。

管理画面でWordPress アドレスを編集する

管理画面で「設定 > 一般設定」でWordPress アドレス (URL)サイトアドレス (URL)を編集します。

読み込んでいるファイルのパスを編集する

テーマファイルでjQueryなどjavaScriptファイルを読み込んでいる場合はスキームをhttp://ではなくhttps://に変更してください。
jQueryなどのパスがhttp://のままだと正しく動作しません。

他にもCSSで画像等を読み込んでいる場合もhttps://へ変更してください。

ちなみにスキームの指定は省略しても動作します。たとえば以下のような指定をする場合、

http://example.com/hoge.js
https://example.com/hoge.js

スキームを省略して以下のように記述することもできます。このように書いておくことでhttpでもhttpsでもどちらも対応することができます

//example.com/hoge.js

そもそも、相対パスで書けるところは相対パスで記述しておくほうがおすすめです。

Search Regexで投稿の内容を一括置換する

投稿の内容に関しては手動ですべて置き換えるというのは現実的ではありません。

WordPressのプラグインで正規表現で置き換えを行うことのできるSearch Regexを利用して一括で置換を行います。
WordPressの管理画面から「プラグイン > 新規追加」で検索ボックスに「Search Regex」と入力してインストールしてください。

有効化したら「プラグイン > Search Regex」と進みます。

Search pattern検索する文字列Replace pattern置き換える文字列を指定します。
Search」ボタンをクリックすれば置き換える文字が何回ヒットしたかカウントしてくれます。(この時点ではまだ置き換えられません)
この処理は重いので処理する文字数が多い場合や、低速なサーバ等の場合は「Limit to」で一度に置き換える文字数を制限したほうが良いかもしれません。

以下のように項目が表示されるので、確認してください。

良いようなら「Replace & Save」ボタンをクリックして置き換えます。
(上にも書きましたが置き換えを行う前にデータベースのバックアップを取ることを強くお勧めします)


以上でLet’s EncryptでWordPressのサイトをhttps化することができました。
Let’s Encryptの導入は「誰でも簡単に利用できる」とは言えないものの、自分でサーバを構築できる人にとっては比較的容易だと思います。
このようなサービスが広まることでインターネットが安心安全に利用できるようになるのは、ありがたい限りです。