nginxによるバーチャルホスト/リバースプロキシによる Let's Encrypt 証明書の取得方法など

# nginxによるバーチャルホスト/リバースプロキシによる Let's Encrypt 証明書の取得方法
今回の目的；
　nginxでバーチャルホストしているドメインに対してLet's Encrypt 証明書を設定したい。

戯言；
　以前より我が家では、勉強用のためにサブドメインや複数ドメインでサーバ振り分けするために nginxリバースプロキシを使っています。たまたま Nextcloud をインストールしたので、Let's Encrypt 証明書を入れてみよう・・・うまくいかなかった・・・。ということがあり今回少し調べてみた感じとなります。

ちなみに我が家は、下記のような構成になっています。hoge ドメインを例としてしていますが実際は異なります。ちなみに Let's Encrypt 証明書は .org に入れる手順となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44540/dd9753a4-2151-69f6-86e9-c3bed1792c85.png)

# Let's Encrypt 証明書について
wikiから転用「Let's Encryptは、非営利団体のInternet Security Research Groupにより運営されている証明書認証局で、TLSのX.509証明書を無料で発行している。証明書の有効期間は90日で、期間内のいつでも証明書の再発行を行うことができる」

ちなみに、本物のSSLサーバ証明書は、2020年9月時点で最大有効期間397日（13ヶ月）となっているため個人で使用するならどちらの証明書を使用してもよいと思います。

## ドメイン認証方法
certbot は、HTTP-01 チャレンジ/DNS-01 チャレンジ の認証があり certbot はどちらも対応しています。DNS-01 チャレンジは、ワイルドカード証明書を取得する際に使用しますが少し手間がかかりますので、今回は `HTTP-01 チャレンジ` を使用したいと思います。
あと HTTP-01 チャレンジ の認証方式はどんな感じなのかという疑問については、下記サイトをみると分かりやすいと思います。

https://balloon-jp.vercel.app/lets-encrypt/#%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E8%AA%8D%E8%A8%BC%E6%96%B9%E6%B3%95


このHTTP-01 チャレンジですが、ピンとくるまで？？？でした。腹落ちしたのは以下の文章でした（原文少し変えています）。
　① certbot が ドキュメントルート/.well-known/acme-challenge/TOKEN を生成する。
　② certbot が Let's Encrypt認証局にリクエストする。
　③ Let's Encrypt が、http://YOUR_DOMAIN/.well-known/acme-challenge/TOKEN を参照しにくる。
　④ certbot は、③の参照が正常である場合、証明書を発行する。

## nginx の設定
`HTTP-01 チャレンジ` とする場合、上記③のとおり HTTP(80)が空いていることが前提です。`hoge.org` を Let's Encrypt 証明書として取得する場合は、下記のように nginx の設定を行います。
その前に certbot が ドキュメントルート以下に /.well-known/acme-challenge/TOKEN を生成するため certbot 実行前に少しおまじないをしておきます。

```bash
$ sudo mkdir /usr/share/nginx/html/ssl-proof
$ sudo chmod 755 ssl-proof
```

nginx の設定も行います。

```nginx:/etc/nginx/conf.d/hoge.org.conf
server {
    listen 80;
    server_name .hoge.org;

    location ^~ /.well-known/acme-challenge/ {
        root   /usr/share/nginx/html/ssl-proof;
    }

    location = /.well-known/acme-challenge/ {
        return 404;
    }
}
```

## certbotクライアントを実行
certbotクライアント は、OS毎にインストール方法が若干異なるのでここでは割愛しインストール済みとします。また certbot のコマンドオプションでは、複数サブドメインの指定もできます。
当然ですが、ここで指定するFQDNはインターネット側から引けるようにDNS設定済みであることが前提となります（A/CNAME どっちでもいいです）。

```bash
$ sudo certbot certonly --webroot -w /usr/share/nginx/html/ssl-proof -d hoge.org -d www.hoge.org
```

結果certbotが成功したとき

```text
/etc/letsencrypt
├── /archive
│   └── /hoge.org
│     └── /cert1.pem　　※サーバ証明書
│     └── /chain1.pem　　※中間証明書
│     └── /fullchain1.pem　　※（中間証明書＋サーバ証明書）連結証明書
│     └── /privkey1.pem　　※秘密鍵
└─── live/
   └── hoge.org
       ├── cert.pem -> ../../archive/hoge.org/cert1.pem
       ├── chain.pem -> ../../archive/hoge.org/chain1.pem
       ├── fullchain.pem -> ../../archive/hoge.org/fullchain1.pem
       └── privkey.pem -> ../../archive/hoge.org/privkey1.pem
```

archive 以下にあるファイルが本体となります。ただ見てわかるようにファイル末尾に番号があります。これは更新などにより加算されていく仕様のため直接参照すると都度修正が発生し面倒です。
そんなわけで通常は、live 以下にあるシンボリックリンクされたファイルを使用するのが普通だそうです。ちなみに live は、certbot が成功するたびに シンボリックリンク先が更新される仕組みとなっています。

## nginxへの証明書の設定
上記にも記載しましたが、nginx で指定する証明書ファイルは、シンボリックリンクの live 先を指定します。そのため ssl_certificate_key には、サーバ証明書を指定するのですが nginx は、連結ファイルと言われる（中間証明書＋サーバ証明書）を指定するのがよいみたいですね。

```nginx:/etc/nginx/conf.d/hoge.org.conf
server {
    listen         443　ssl;
    server_name    *.hoge.org;

    ssl on;
    ssl_certificate     /etc/letsencrypt/live/hoge.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge.org/privkey.pem;
}
```

# httpsリバースプロキシの設定
最後の目的である、httpsリバースプロキシの設定をしていきます。といっても普通にSSL証明書の設定するのと同じとなるため面白味にかけると思うので、[SSL Server Test](https://www.ssllabs.com/ssltest/) で怒られないぐらいの評価とれるっていうぐらいには設定してみます。

ちなみリバースプロキシ先も、SSL化しています。なんでもオーバーヘッドがあるからおすすめしないとかあるようですが勉強用だからよしとしてます。`100.100.0.2` 先・・・apache ですが、SSL証明書の設定は・・・割愛しますね。

```nginx:/etc/nginx/conf.d/hoge.org.conf
server {
    listen         443　ssl;
    server_name    *.hoge.org;

    ssl on;
    access_log  /var/log/nginx/https.access.log;

    ssl_certificate     /etc/letsencrypt/live/hoge.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge.org/privkey.pem;

    add_header X-XSS-Protection "1; mode=block";
    add_header X-Frame-Options SAMEORIGIN;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'HIGH !aNULL !eNULL !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !RSA !AES128 !ARIA128';

    server_tokens off;

    ssl_session_cache shared:SSL:10m;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header    Host            $host;
    proxy_redirect      off;
    proxy_max_temp_file_size    0;

    location / {
        proxy_pass https://100.100.0.2;
    }
}
```

# Let's Encrypt を導入するにあたって
自分が食わず嫌いだけだったのかもしれませんが、比較的割と簡単に Let's Encrypt を導入できると感じました。しかも有効期限が短いとはいえ本物の証明書のため開発時には「Let's Encrypt を使用」し本番には、「本物の証明書」を切り替えるにもよいかなと。

あとここまで自分で設定する必要もない [Cerbot-Docker](https://hub.docker.com/r/certbot/certbot/) 使用すれば、より開発者は環境回りを気にせず開発に専念できるのかなーとも思いました。

ともかくオレオレ証明書作成する手間ぐらいで作成できる、というのは大きいですね。

# 参考にさせて頂いたサイト様

https://qiita.com/HeRo/items/f9eb8d8a08d4d5b63ee9

https://qiita.com/ukitazume/items/5c8a51e441eb09769873

https://www.kagoya.jp/howto/it-glossary/security/lets-encrypt/

https://hub.docker.com/r/certbot/certbot/