Let's Encrypt で手軽に HTTPS サーバを設定する

[追記] **この記事にはかなり古い情報が掲載されています。**

Certbot を使うことで更に手軽に設定することが出来ますので、お試しください。
https://certbot.eff.org/

良い時代になりましたね。
※コメントでのご指摘ありがとうございました。

---

昨日、 Let's Encrypt が Public Beta になり、申請不要で誰でも利用できるようになりました。

[Entering Public Beta](https://letsencrypt.org/2015/12/03/entering-public-beta.html)

Let's Encrypt とは
====

> Let’s Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit. Let’s Encrypt is a service provided by the Internet Security Research Group (ISRG).
https://letsencrypt.org/about/

> (和訳)
> Let's Encrypt は無料で自動化されたオープンな認証局 (CA) で、公共の利益の為に運営されています。Internet Security Research Group (ISRG) が提供するサービスです。

Free, Automatic, Secure, Transparent, Open, Cooperative を特徴として挙げています。詳しくは [Web](https://letsencrypt.org/about/) で。

一言で表すと、ドメイン認証の SSL サーバ証明書が無料で簡単に発行できるサービスと言った印象です。

現在、サービスを HTTPS で提供する需要は高まっています。
例えば、次に挙げるものは HTTPS での接続が必要となる例です。

* HTML5 Service Workers
* HTTP/2 over TLS (h2)
* iOS 9 App Transport Security

他にも、 Chromium では Device motion / orientation, EME, Fullscreen, Geolocation, getUserMedia() などの機能利用するためには HTTPS が必須になると言われています[^1]。

ですが、 HTTPS で安全にサービスを提供するためには、信用できる認証局 (CA) の発行する SSL サーバ証明書が必須です。これまでも SSL サーバ証明書は無料で取得できましたが[^2]、 CSR を生成するなどの手間が多く、手軽ではありませんでした。

Let's Encrypt を使えば、手軽に今日から HTTPS でサーバを設定・運用する事が可能です。

※ドメイン認証の証明書では、ドメインの所有者であることを証明することは出来ますが、運営者や運営組織の証明とはならないので、 EC サイトには向かないでしょう。
一般的な Web サイトや、お問い合わせフォーム、オンラインゲーム、お金やクレジットカード情報を直接取り扱わない Web サービス等に適しています。

[^1]: https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins
[^2]: https://ww24.jp/security/startssl-class2-certificate/

Let's Encrypt を始める
====
## 必要なもの
* Git
* OpenSSL
* Web Server (nginx, apache など)

Let's Encrypt を利用するために必要なパッケージ等は Let's Encrypt のインストール時に自動的に入ります。
OpenSSL は特別な理由が無い限り常に最新に更新しておきましょう。

## 手順
1. 運用したいドメインを取得します。
2. 運用したいドメイン(サブドメイン)から名前解決できるよう、 DNS で A, AAAA レコードや CNAME レコードを設定します。
3. 運用したいドメインに対して、サーバでバーチャルドメインの設定をします。
4. Let's Encrypt をインストールします。
5. SSL サーバ証明書を取得します。
6. SSL サーバ証明書をインストールします。

この内、 1, 2 は省略します。利用しているサービスに合わせて設定してください。

## バーチャルドメインの設定 (for nginx)
Apache や他の Web Server の場合はそのサーバに合わせて設定してください。

※ 以下の `user` と `encrypt.example.com` は環境に合わせて適宜変更してください。

```nginx:/etc/nginx/sites-available/encrypt.example.com
server {
    listen 80;
    server_name encrypt.example.com;
    root /home/user/encrypt.example.com;
}
```

最低、これだけ設定されていれば良いです。
忘れずに `/etc/nginx/sites-enabled` にシンボリックリンクを作成しておきましょう。

```shell-session
cd /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/encrypt.example.com .
```

最後に、ドキュメントルートに設定したディレクトリを作成して、 nginx を再起動しましょう。

```
mkdir ~/encrypt.example.com

# 設定のチェック
sudo nginx -t
# 再起動
sudo service nginx restart

# systemd の場合
# sudo systemctl restart nginx
```

## Let's Encrypt のインストール
ユーザ権限で読み書き出来る場所 (ホームディレクトリ) 等で `git clone` しましょう。

```
git clone https://github.com/letsencrypt/letsencrypt.git
```

`letsencrypt-auto` コマンドが用意されているので、まずは help を叩いてみます。
ここで、まだインストールされていない必要なパッケージがあれば自動的にインストールされます。(root 権限が要求されます)

```
cd letsencrypt
./letsencrypt-auto --help
```

## SSL サーバ証明書の取得
現状、 apache, standalone, webroot の3つのプラグインが提供されており、 apache の場合は簡単にインストールが可能です。ここでの説明は省略します。

### standalone
スタンドアロンサーバがポート 80 で立ち上がり、 Let's Encrypt の認証サーバに対して認証ファイルを返します。しかし、既に apache や nginx などの Web Server がポート 80 を使っている場合は、一度稼働しているサービスを止める事になり、ダウンタイムが発生します。

### webroot
ドキュメントルートを指定すると認証ファイルが配置され、既存の Web Server を通じて Let's Encrypt の認証サーバへ認証ファイルを返します。既にポート 80 で稼働しているサービスを停止すること無く認証が可能なので便利です。Web Server の設定変更を反映する際にダウンタイムが発生しますが停止するよりも短く済みます。

ここでは webroot プラグインを用いた方法を紹介します。

`--webroot-path` オプションでドキュメントルートを指定します。

```
./letsencrypt-auto certonly --webroot --webroot-path ~/encrypt.example.com -d encrypt.example.com
```

実行して、以下のような結果が表示されれば成功です！

```
Updating letsencrypt and virtual environment dependencies.......
Running with virtualenv: sudo /home/user/.local/share/letsencrypt/bin/letsencrypt certonly --webroot --we
broot-path /home/user/encrypt.example.com -d encrypt.example.com

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/encrypt.example.com/fullchain.pem. Your cert will
   expire on 2016-03-03. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If like Let's Encrypt, please consider supporting our work by:

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

証明書は `/etc/letsencrypt/live/encrypt.example.com/fullchain.pem`
秘密鍵は
`/etc/letsencrypt/live/encrypt.example.com/private.pem`
を利用します。

## SSL サーバ証明書を nginx へ設定

早速、 nginx に設定しましょう。
その前に、準備として Diffie-Hellman パラメータを生成しましょう。
少し時間が掛かりますが、 [Diffie-Hellman 鍵共有](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%95%E3%82%A3%E3%83%BC%E3%83%BB%E3%83%98%E3%83%AB%E3%83%9E%E3%83%B3%E9%8D%B5%E5%85%B1%E6%9C%89) で必要となるパラメータで安全性に関わる為、この処理は必ず実行しましょう。

複数サイトを運用する場合、この処理は1度だけで大丈夫です。漏洩しないように注意しましょう。

```
sudo openssl dhparam -out /etc/ssl/private/dhparam.pem 2048
```

先程の設定ファイルを編集します。

```nginx:/etc/nginx/sites-available/encrypt.example.com
# HTTP で接続があった際に HTTPS へリダイレクトする為の設定
server {
    listen 80;
    server_name encrypt.example.com;
    rewrite ^ https://$server_name$request_uri? permanent;
}

# HTTPS サーバの設定
server {
    listen 443 ssl;
    # nginx 1.9.5 以降の場合
    # listen 443 ssl http2;

    server_name encrypt.example.com;

    ssl_certificate /etc/letsencrypt/live/encrypt.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/encrypt.example.com/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    # 環境によっては off にすると動かないので注意 (default は on)
    ssl_session_tickets on;

    # 2048bit 推奨
    ssl_dhparam /etc/ssl/private/dhparam.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;

    # HTTPS のみでサービスを提供する場合にだけ設定します
    add_header Strict-Transport-Security max-age=15768000;

    # HPKP (HTTP Public Key Pinning) に対応すると更に安全性の高い設定になりますが、運用コストが上がるのでここでは省略します。

    # OCSP Stapling に対応すると暗号化通信の開始を早めることが出来ます。ここでは省略します。

    # ドキュメントルート
    root /home/user/encrypt.example.com;
}
```

あとは nginx を再起動したら終わりです！

```
sudo nginx -t
sudo service nginx restart

# systemd の場合
# sudo systemctl restart nginx
```

お疲れ様でした。

最後に、ドキュメントルートに適当な HTML を設置してブラウザで開いてみます。

![緑色の鍵](https://qiita-image-store.s3.amazonaws.com/0/1804/bf97d67d-e222-0f08-6696-624849ab19da.png)

![証明書情報](https://qiita-image-store.s3.amazonaws.com/0/1804/891d55c6-cdd2-1c24-3515-e44c6ef22caf.png)

このように緑色の鍵が表示され、証明書情報をクリックして証明書チェーンが表示されていれば正しく設定されています。

90日で有効期限が切れるので、先程と同様に `letsencrypt-auto` コマンドを叩いて nginx を再起動することで証明書が更新されます。

非常に手軽で便利ですね！

おまけ
====
設定したサーバの安全性をチェックしましょう。

Qualys SSL Labs の [SSL Server Test](https://www.ssllabs.com/ssltest/) を利用します。

2015年12月5日現在、結果は A+ でした。

> ![SSL Report: encrypt.ww24.jp](https://qiita-image-store.s3.amazonaws.com/0/1804/d4075508-8b90-a2e4-ccdd-a7cc2055f0e1.png)
[SSL Server Test: encrypt.ww24.jp (Powered by Qualys SSL Labs)](https://www.ssllabs.com/ssltest/analyze.html?d=encrypt.ww24.jp&s=160.16.52.211&hideResults=on)

診断結果は、新たな脆弱性が発見されて対策しなければ下がります。
常に安全なサーバ運用を心掛けましょう。

参考
====
* [GitHub letsencrypt/letsencrypt](https://github.com/letsencrypt/letsencrypt)
* [Let’s Encrypt client documentation](https://letsencrypt.readthedocs.org/en/latest/)
* [Generate Mozilla Security Recommended Web Server Configuration Files](https://mozilla.github.io/server-side-tls/ssl-config-generator/)