Let's Encryptを使って無料でnginxをHTTPS対応させる

nginxでサーバを運用していますが、今更ながらにHTTPS(SSL)対応しました。

そもそも今までSSL化していなかった理由は、

  1. ちゃんとした認証局で証明書を作るとお金がかかる
  2. 自己証明書(通称オレオレ証明書)でHTTPS対応するくらいならHTTPでいい

という思いからでした。

昔、自宅サーバを運用していたときには、勉強を兼ねてオレオレ証明書HTTPSを使っていました。 その頃は、勉強用の自宅サーバという側面から、自分以外の利用者を想定する必要がなかったのでそれで問題ありませんでしたが、一般公開用のブログとしては不適当だと考えていました。

また、証明書にはお金がかかると思っていたので、収益化できていないブログに対してこれ以上費用をかけてもな…という考えでした。(ドメインにもサーバにも費用がかかってますので)

それでも一応、どれくらいの費用がかかるのだろうと調べてみたところ、Let's Encryptなら0円で証明書が発行できるということを知りました。 費用がかからないのなら、後回しにする理由はない!ということで、HTTPS対応に挑戦してみました。

Let's Encryptとは

無料でHTTPSが使えるとはいっても、怪しい団体の証明書だったらオレオレ証明書と変わらない、いやもっと危ない、と思いLet's Encryptについて調べてみました。

公式サイトによると運営団体は、Internet Security Research Group(ISRG)ということですが、その賛同団体・スポンサーの一覧を見ると、私の耳馴染みのあるものだけでも、mozilla, CISCO, chrome, facebook, GitHub, verizon... と錚々たる企業・ブランドが並んでいますね。

これらの企業の支援のもとで、すべてのサイトをHTTPS化するのを目的として、活動しているようです。

その目的に見合うように、費用がかからないだけでなく手順も非常に簡単になっていました。

それでは実際にHTTPS化してみます

以下のQiita記事がとてもわかり易かったです。 Let's Encrypt で Nginx にSSLを設定する - Qiita

しかしいくつか躓いたところがありましたので、そのあたりを補足しながら手順を書いていきます。

なお、動作環境は次のとおりです。

  • OS: CentOS 7.6.1810
  • webサーバ: nginx 1.14.2

また、証明書を取得したいドメインで、80番ポートでアクセスできる環境が必要になります。

certbotのインストール

Let's Encryptのクライアントcertbotをインストールします。 yumあたりでするのかと思ったら、gitで持ってくるようです。

先程のQiita記事に従ってインストールしました。

[user@server ~]$ cd /usr/local/
[user@server local]$ ls
bin  etc  games  include  lib  lib64  libexec  sbin  share  src
[user@server local]$ git clone https://github.com/certbot/certbot
-bash: git: command not found

ええ、gitが入っていませんでした…。 yumでgitをインストールします。

[user@server local]$ sudo yum install git
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
   中  略
Installed:
  git.x86_64 0:1.8.3.1-20.el7

Dependency Installed:
  perl-Error.noarch 1:0.17020-2.el7              perl-Git.noarch 0:1.8.3.1-20.el7              perl-TermReadKey.x86_64 0:2.30-20.el7

Complete!

改めて、git clone します。

[user@server local]$ sudo git clone https://github.com/certbot/certbot
Cloning into 'certbot'...
remote: Enumerating objects: 29, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 61386 (delta 8), reused 5 (delta 2), pack-reused 61357
Receiving objects: 100% (61386/61386), 20.07 MiB | 8.37 MiB/s, done.
Resolving deltas: 100% (44598/44598), done.

これだけでインストールが完了です。 とても手軽!

証明書の取得

ここからは、ドメイン名が出てきます。 example.com として記載しますので、実際に行う際には適宜置き換えてください。

証明書の取得位は次のコマンドで行います。 ただし、このコマンドを実行する前にWebサーバを停止させる必要があります。私はサーバを停止させずに実行したので、以下の例は失敗例となります。(コマンド自体は同じです)

[user@server local]$ /usr/local/certbot/certbot-auto certonly --standalone -t

このコマンドを実行すると、内部でyumが動作し大量のパッケージがインストールされます。 提供元を使用していないと怖くてしょうがないレベルですね。 yumの定番 Is this ok [y/d/N]: y で問われますので y と入力しインストールを許可しましょう。

次に、メールアドレスを聞いてきます。

Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): user@example.com

その次は利用規約に同意するか問われます。 当然、同意しないと先には進めないのでAgreeの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-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

この次が、Qiita記事と違いました。 次のような確認メッセージが出ます。 ざっくり意訳すると、登録したメールアドレス宛にメールマガジンを送るか?といった感じでしょうか。 試しにNoにしてみましたが、証明書取得には問題ありませんでした。 もちろんメールマガジンに興味ある方はYesを選択して良いと思います。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N

この次は、Qiita記事に合流する感じで、ドメインの入力を求められました。

Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel): example.com

ここで、上述したWebサーバを止めていなかったため失敗になりました。

Obtaining a new certificate
Performing the following challenges:
http-01 challenge for example.com
Cleaning up challenges
Problem binding to port 80: Could not bind to IPv4 or IPv6.

IMPORTANT NOTES:
 - 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.

赤字部分ですね。 実際のログでは色が変わっていなかったので、最初気づきませんでした。 IMPORTANT NOTESがQiita記事より少ないなぁとは思いましたが、メールマガジン云々同様記事が書かれてからの変化かと流して次のnginx設定に進んでしまい、あとで戻ることになりました。

どうやら、certbotが80番ポートで、認証局側からのアクセスを待ち受けて証明書を作るような動きをしているようです。 そのため、Webサーバなどが80番ポートを使っていると、このエラーが出るようです。

これを解決するため、Webサーバを停止させます。

[user@server local]$ sudo /bin/systemctl stop nginx

改めて証明書作成をします。

コマンドは先程と同じです。先程聞かれたメールアドレスや規約同意の確認などは不要でした。 聞かれるのはドメイン名のみ。

[user@server local]$ /usr/local/certbot/certbot-auto certonly --standalone -t
Requesting to rerun ./certbot-auto with root privileges...
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel): example.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for example.com
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 2019-03-14. 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

これで証明書が取得できました。

Webサーバで使用する証明書の格納場所は、上のメッセージに記載されている以下の2つです。 /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/example.com/privkey.pem

nginxの設定

取得した証明書をnginxが使えるように設定ファイルを書き換えます。 赤字が削除ヶ所、青字が追記箇所です。

Qiita記事での、HTTPSを有効にするために書かれている項目と、更新自動化のために書かれている項目、SSL安全性テストA+が取れるようにした項目を合わせ込んであります。

/etc/nginx/conf.d/default.conf

server {
    listen    80;
    listen    443 ssl;
    server_name localhost example.com www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_prefer_server_ciphers  on;
    ssl_ciphers  'ECDH !aNULL !eNULL !SSLv2 !SSLv3';
    add_header  Strict-Transport-Security "max-age=31536000; includeSubdomains";
        

    root /usr/share/nginx/wordpress;
    index index.php

    charset utf-8;

    location / {
      try_files $uri $uri/ @wordpress;
    }

    location ~ \.php$ {
      try_files $uri @wordpress;
      fastcgi_index index.php;
      fastcgi_split_path_info ^(.+\.php)(.*)$;
      fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
      fastcgi_param SCRIPT_FILENAME /usr/share/nginx/wordpress$fastcgi_script_name;
      include fastcgi_params;
    }

    location @wordpress {
      fastcgi_index index.php;
      fastcgi_split_path_info ^(.+\.php)(.*)$;
      fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
      fastcgi_param SCRIPT_FILENAME /usr/share/nginx/wordpress/index.php;
      include fastcgi_params;
    }

    access_log /var/log/nginx/wordpress.access.log main;

    error_page  404              /error/404.html;
    error_page   500 502 503 504  /error/50x.html;

}


server {

    listen    80;
    location ^~ /.well-known/acme-challenge/ {
      default_type "text/plain";
            root         /path/to/doc/root/;
    }

    location / {
      # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
      return 301 https://$host$request_uri;
    }

}

このnginx設定でいくつか注意事項があります。

  1. HTTPに来たアクセスを(certbotの更新を除き)すべてHTTPSにリダイレクトする設定となっています。
  2. 下部の方にある緑字の/path/to/doc/root/は、証明書の更新時に使われるディレクトリとなります。適当なディレクトリを作り、そこを指定すれば良いようです。私は/usr/share/nginx/certbot/としました。
  3. 安全性A+にすることで、古いブラウザやプログラムからアクセスできなくなる可能性があります。 その場合は、以下の参考元サイトを参照し適宜修正してください。 Webサーバー nginx における SSL証明書設定の安全性向上 ~SSL Server Test で A+ 判定を目指して~ | SaintSouth.NET

設定ファイル更新が完了したらnginxを立ち上げます。

[user@server local]$ sudo /bin/systemctl start nginx

https://example.com でアクセスできること、http://example.com でアクセスするとhttps://example.com にリダイレクトされること、(環境ができていれば)httpのサブディレクトリやファイルへのアクセスもhttpsにリダイレクトされること、くらいを確認すると良いでしょう。

自動更新設定はまた次回

上記までで、HTTPS対応はひとまず完了です。 しかし、Let's Encryptで取得した証明書は期限が90日なので、油断するとあっという間に期限切れになってしまいます。 自動更新設定をしておくことで、この問題が発生しないようにしておくのがよいでしょう。

Qiita記事に書いてあった更新手順を試してみてはいますが、証明書が新しすぎるため更新がスキップされ成否の判断がまだできていません。

こちらはまた時間が経ったときに試して、まとめてみたいと思います。

自動更新について掲載しました

https://yuuchika.com/lets_encrypt_nginx_renewal/

おまけ - WordPressの設定

HTTPSに切り替えたことで、WordPressに不具合が出ていないかすこし試してみました。 軽く動作を見たところ、特に問題はなさそうです。

一応、設定->一般 から 「WordPressアドレス(URL)」と「サイトアドレス(URL)」をhttpsに変更しましたが、変更前(かつHTTP→HTTPSのリダイレクト設定前)でも問題はありませんでした。