ASP.NET Core Blazor と Chart.js で入門するWebアプリ作成 － 番外編：Ubuntuサーバで公開する

標題記事の最終回。各回記事の内容は以下のようになります。

[導入編：サンプルを動かす][link-1]
[探究編：Blazorの仕組みを理解する][link-2]
[実践編：Chart.jsでグラフを描く][link-3]
[Tips編：BlazorやChart.jsのTipsなど][link-4]
番外編：Ubuntuサーバで公開する（当記事）

[link-1]:https://qiita.com/okatako/items/171f05dfc36d6b27769d
[link-2]:https://qiita.com/okatako/items/c0208510961982df4bdc
[link-3]:https://qiita.com/okatako/items/4dbb7eaee95f4c337d57
[link-4]:https://qiita.com/okatako/items/e50b90afecaf5ddb5f01

今回の内容は、第1回の「作業の流れ」で提示した項目のうち「9. 発展編（Ubuntuサーバでアプリを公開する）」となります。今回の記事には Blazor や Chart.js の話はほとんど出てきませんので「番外編」としました。あらかじめご承知おきください。

[^bangai]: 今回の記事は内容からすると「番外編」とすべきだったかも。

# 公開までの作業の流れ

まず外部に Ubuntu サーバが用意できていることを前提とします。またそのサーバマシンにはグローバルIPが割り当てられており、適切な FQDN で DNS により正引きできることが必要です。本稿では、AWS EC2 で作成した Ubuntu サーバ[^aws-ubuntu]での実行結果を基に記述することにします。

[^aws-ubuntu]: 筆者は今回の記事を書くにあたり、AWS EC2 で Ubuntu 18.04 を利用しました。お試し用ならば t2.nano のオンデマンドで十分であると思います。あとは使い捨てるつもりで当初使用価格が格安のドメインを購入すれば、都合500円くらいの費用で試してみることができると思います。DNS についてはドメイン業者がサービスを提供しているし、あるいは AWS Route53 を使ってもよいでしょう。

サーバマシンは、ポート 80(HTTP) と 443(HTTPS) が開いていることを確認しておいてください。昨今の Web 環境は SSL/TLS化が当たり前になっています。また、ローカル環境からは ssh および scp でアクセスできることも前提とします。

Blazor で作成した Webアプリは、デフォルトだと HTTP を 5000番ポートで、HTTPS を 5001番ポートで LISTEN します。そして HTTP (5000) にきたリクエストは HTTPS (5001) にリダイレクトされます[^UseHttpsRedirection]。

[^UseHttpsRedirection]: _Startup.cs_ の `Configure()` で `app.UseHttpsRedirection();` を呼んでいる場合。ただしサーバに「開発証明書」がインストールされていないと HTTPS(5001)のほうは LISTEN されず、したがってリダイレクトもされません（警告が表示される）。

Blazor アプリの Web サーバ部分は Kestrel というプログラムが担っています。以下、「[リバース プロキシ サーバーを利用する](https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#use-a-reverse-proxy-server)」から引用します。

>Kestrel は、ASP.NET Core から動的なコンテンツを提供するのに役立ちます。 ただし、Web サーバーとしての機能は、IIS、Apache、Nginx などのサーバーと比べると制限されます。リバース プロキシ サーバーは、静的コンテンツ サービス、要求のキャッシュ、要求の圧縮、HTTP サーバーからの HTTPS 終了などの作業の負荷を軽減します。 

そういうわけで、 Blazor アプリ自体はデフォルトの設定でバックエンドとして稼動させ、フロントエンドに別のリバースプロキシを立てて、80/443 へのリクエストをバックエンドの kestrel に流すような仕組みが推奨されています。本記事では、リバースプロキシとして nginx を用いることにします。

これをふまえて、以下のような流れで作業することにします。

1. 外部 Ubuntu サーバへの .NET Core のインストール
2. ローカル環境で製品版をビルドしリモ一トサーバにコピー
4. テスト実行
5. nginx のインストール
6. nginx でリダイレクトとリバースプロキシの設定
7. SSL化を自己署名証明書でテスト
7. Let's Encrypt からSSLサーバ証明書を取得して本番稼動


# Ubuntu サーバへの .NET Core のインストール
Microsoft の「[Ubuntu に .NET Core SDK または .NET Core ランタイムをインストールする](https://docs.microsoft.com/ja-jp/dotnet/core/install/linux-ubuntu)」を参照しながら作業を進めます。今回は [18.04 (LTS)](https://docs.microsoft.com/ja-jp/dotnet/core/install/linux-ubuntu#1804-)を対象としますが、実際には自身の環境に合ったパッケージを選んでください。基本的にMSのドキュメント通りに作業すれば問題なくインストールできると思います。

（１）信頼されたキーの一覧に Microsoft パッケージ署名キーを追加し、パッケージ リポジトリを追加

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/4a1f07c7-7136-3149-2b0c-542ef2d75755.png)

（２）SDK のインストール

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo apt-get update; \
  sudo apt-get install -y apt-transport-https && \
  sudo apt-get update && \
  sudo apt-get install -y dotnet-sdk-3.1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/85ec85f4-e9cb-7f1c-b837-651e8ac31af4.png)

（３）確認

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
dotnet --version
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/031ff1ad-0ae0-5eb9-f860-b686cb360c70.png)
何か問題が発生したら、上記 Microsoft のドキュメントページを参照してください。

# ローカル環境で製品版をビルドしリモ一トサーバにコピー
（１）外部 Ubuntu サーバに、Blazor アプリ一式を格納するディレクトリを作成しておきます。任意の場所で構いませんが、本記事では、`~/dotnet` とします。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
mkdir -p ~/dotnet
```

（２）ローカルの開発環境で製品版(Release版)をビルド
ローカルPCのプロジェクトフォルダで `dotnet publish -c release` を実行し、製品版(Release版)をビルドして、実行に必要なファイルを `publish` フォルダにまとめます。

```shell-session:Git&nbsp;Bash：/e/Dev/dotnet/MyBlazorApp
dotnet publish -c release
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/89d46e47-4d1e-24ba-a2b4-2ef1b3e3941f.png)

（３）`publish` フォルダを remote Ubuntu の `~/dotnet` にコピー
実行に必要なファイルは `bin/Release/netcoreapp3.1/publish` にまとまっているので、下記コマンドを実行して、リモートサーバの `~/dotnet/MyBlazorApp` にコピーします。ここで `$PRIVATE_PEM` にはリモートサーバにSSHアクセスするための秘密鍵のパスが、また `$REMOTE_HOST` にはリモートサーバーのドメイン名またはグローバルIPが設定されているものとします[^aws-ubuntu-user]。

[^aws-ubuntu-user]: AWS EC2 の Ubuntu インスタンスでは、ログインユーザ名が `ubuntu` になるようです。

```shell-session:Git&nbsp;Bash：/e/Dev/dotnet/MyBlazorApp
scp -rp -i $PRIVATE_PEM bin/Release/netcoreapp3.1/publish ubuntu@$REMOTE_HOST:~/dotnet/MyBlazorApp
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/b80b5ede-479c-1409-ad2b-9c7e674fd8d8.png)

（４）リモートサーバでファイルを確認
リモートサーバで `ls -l ~/dotnet/MyBlazorApp` を実行して確認します。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
ls -l ~/dotnet/MyBlazorApp
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/e3568cf6-35e2-cfda-35c1-f3c8d1b39720.png)

# テスト実行
発行されたアプリを実行するには `dotnet MyBlazorApp.dll`[^change_workdir] を実行します。リモートサーバで、まずはそのまま実行してみましょう。

[^change_workdir]: カレントディレクトリを変更してから実行します。Blazor はカレントディレクトリにある _wwwroot_ を見るようです。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
cd ~/dotnet/MyBlazorApp
dotnet MyBlazorApp.dll
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/2e359329-6544-69a3-f013-99e1385682d5.png)
警告が出ていますが、初回起動時は DataProtection のためのキーが存在しないので、平文のままキーファイルを作成するよ、ということのようです。2回目からはこの警告は出なくなります。

メッセージを見ると HTTP(5000番ポート)をLISTENしていることが分かります。Ubuntu用の ASP.NET Core には「HTTPS開発証明書」(HTTPS developer certificate)が含まれていないようです。Kestrel は HTTPS開発証明書が存在しない場合は HTTPS(5001) をLISTEN しません。

もし Kestrel に HTTPS(5001)でも LISTEN させたい場合は、次のコマンドを実行して開発証明書をインストールしてください。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
dotnet dev-certs https
```

本稿では nginx をリバースプロキシとして用い Kestrel の 5000 ポートに接続するような構成を取るので、このままの状態で先に進みます。

ここで別のターミナルを開いてリモートサーバに接続[^use-screen]し、`wget http://localhost:5000/` を実行してみます[^wget-https]。

[^use-screen]: `screen` をご存じの方は、いったん ^C でアプリを停止させ、コマンドラインに戻ってから `screen` で複数のスクリーンを開いたほうが便利かもしれません。

[^wget-https]: HTTPS(5001)も有効にしている場合は、 `wget --no-check-certificate https://localhost:5001/` を実行してください。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
wget http://localhost:5000/
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/89ac1a3b-70cf-11a2-9c20-27c0ad951b3d.png)
HTTP(5000)に接続してルートページがダウンロードされました。ダウンロードされた _index.html_ ファイルを vim で開いてみます。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
vim index.html
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/60e0a07e-5f85-b436-89d3-3dc015500f62.png)
まさしく作成した Blazor アプリのルートページがダウンロードされています。

# nginx のインストール
リモート Ubuntu サーバで以下のコマンドを実行してください。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo apt-get update
sudo apt-get install -y nginx
sudo service nginx status
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/19dec0e5-d203-b3e3-2e87-15b39c6c6627.png)
status が <font color="green">**active (running)**</font> になっていれば OK です。

# nginx でリダイレクトとリバースプロキシの設定
以下の2つの設定を行います。詳細については「[リバース プロキシ サーバーを構成する](https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-a-reverse-proxy-server)」も参照ください。

- HTTP(80) へのリクエストはすべて HTTPS(443) にリダイレクトする
- HTTPS(443) へのリクエストはすべてバックエンドの HTTP(5000) へリバースプロキシで転送する

_/etc/nginx/sites-available/default_ を修正します。修正する前にオリジナルの _default_ ファイルを別名で保存しておくとよいでしょう。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
cd /etc/nginx/sites-available
sudo cp -p default default.orig
```
_default_ をエディタで開いて内容をいったん全削除します[^vim-delete-all]。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo vim default
```
[^vim-delete-all]: vim で `:1,$d`&lt;Enter&gt; と打てば全削除できます。`ggVGd` でもOK。

以下の内容をコピペしてください[^vim-paste]。その後 _YOUR.SERVER.FQDN_ のところを Blazor アプリを公開するホストの FQDN（小文字）で置き換えてください。

[^vim-paste]: vim を使っている場合は、`:set paste` を実行してからペーストしてください。

```text:vim&nbsp;on&nbsp;remote&nbsp;Ubuntu
server {
    server_name _;
    listen 80;

    if ($host = YOUR.SERVER.FQDN) {
        return 301 https://$host$request_uri;
    }

    return 404;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    server_name _;
    listen 443 ssl;

    ssl_certificate ssl/server.crt;
    ssl_certificate_key ssl/private.key;

    if ($host != YOUR.SERVER.FQDN) {
        return 404;
    }

    proxy_http_version 1.1;
    proxy_set_header   Host $host;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection $connection_upgrade;

    location / {
        proxy_pass         http://localhost:5000;
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/1b9652f1-0a82-d340-d19b-82fb31389064.png)
これは以下のことを設定しています。

**1～10行：ポート80 に対する設定**

- 2行：任意の名前のバーチャルホスト名を受け付ける
- 3行：ポート 80 を LISTEN する
- 5～7行： リクエストされたホスト名が、用意したFQDNに一致したら https にリダイレクトする
- 9行： 上記FQDN以外のバーチャルホストに対する要求には 404 を返す</dd>

**12～15行： 下記 31行の `Conection` 設定で必要となる変数の用意**

**17～36行： ポート 443 に対する設定**

- 18行： 任意の名前のバーチャルホスト名を受け付ける
- 19行： ポート 443 を SSL で LISTEN する
- 21行： サーバ証明書のパス
- 22行： サーバ秘密鍵のパス
- 24～26行： リクエストされたホスト名が、用意したFQDNに一致しなかったら 404 を返す
- 28～29行： リバースプロキシの際のHTTPバージョンとホスト名の設定
- 30～31行： リバースプロキシで WebSocket を通すために必要な設定[^websocket]
- 33～35行： すべてのパスに対する要求を http&#58;//localhost:5000 に転送する[^ssl-5001]

[^websocket]: 「[Nginx を構成する](https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx)」では `proxy_set_header Connection keep-alive;` になっていますが、その設定では WebSocket がリバースプロキシを通らないようです。http://nginx.org/en/docs/http/websocket.html を参照してください。Qiitaの記事では「[NginxのリバースプロキシでWebソケットを通す際の設定](https://qiita.com/YuukiMiyoshi/items/d56d99be7fb8f69a751b)」が参考になりました。

[^ssl-5001]: もし HTTPS開発証明書をインストールしてあり、HTTPSとして5001ポートもLISTENしている場合は、転送先を https&#58;//localhost:5001 に変更してください。

上記設定ファイルでは、サーバ証明書として、テスト用の自己署名証明書へのパスを設定してあります。次節でその自己署名証明書を作成し、SSL化のテストを行います。

# SSL化を自己署名証明書でテスト
SSL化、リダイレクト、リバースプロキシの設定がうまくいっているかどうかを確認するために、まずは自己署名証明書でテストしてみましょう[^ssl-certificate]。

[^ssl-certificate]: nginx で HTTPS をサポートする場合は、サーバ証明書がないと起動してくれません。

自己署名証明書は通称「オレオレ証明書」とも呼ばれています[^ore-ore]。本来サーバ証明書というのは公的な認証局によって署名してもらう必要があるのですが、その署名作業自体を自分でやってしまうからです。当然どこからもお墨付きをもらっていないので、自己署名証明書を使っているサイトに https でアクセスすると、ブラウザに「接続がプライベートではありません」というような警告が出てしまいます。それでも https でアクセスするテストにはなるので、まずはこれを試してみましょう。

[^ore-ore]: というか「オレオレ証明書」のほうが通りがよいかもしれません。

通常、サーバ証明書は次の3ステップで作成します。

1. サーバ秘密鍵の作成
2. 認証局に対する署名要求書の作成
3. 署名要求書に基づいた認証局による署名

なのですが、世の中にはこの作業を一回のコマンドでやってしまう「オレオレ証明書ワンライナー」という匠の技があります。ググるとたくさん出てくるのですが、今回は「[karakaram-blog](https://www.karakaram.com/creating-self-signed-certificate/)」というサイトで紹介されている下記コマンドを利用することにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/03c2d2a9-c002-5b14-4c5e-bf567f317b95.png)
ちなみに上記サイトでは3ステップによる証明書作成手順についても説明してくれているので、興味のある方は一読されるとよいかと思います。

具体的な手順は次のようになります。

```shell-session:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo mkdir -p /etc/nginx/ssl
cd /etc/nginx/ssl
sudo openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -subj /CN=localhost -keyout private.key -out server.crt
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/024e8f78-23bc-7a2b-880d-270060042de4.png)
_/etc/nginx/ssl_ ディレクトリを作成して、そこで openssl コマンドを実行するわけですが、環境によっては上図のように `Can't load xxx/.rnd into RNG` というようなエラーが出ることがあります。この場合は、 _/etc/ssl/openssl.cnf_ の `RANDFILE = $ENV::HOME/.rnd` のところをコメントアウトしてから再実行してやればよいようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/56e268c1-2d5b-1bde-9434-d9897deb3101.png)
を
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/a5823ac2-eaba-59f2-9139-243573ddf410.png)
に修正してから openssl コマンドを再実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/ead0924f-80c0-f06d-1464-ecdae9ad2bba.png)
今度はうまく行きました。_/etc/nginx/sites-available/default_ に設定しておいた名前でファイルも作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/32aba570-a4c4-9b9b-29ea-582fca879b02.png)
`sudo nginx -t` で nginx の設定ファイルが正しく記述されているかテストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/dd565acc-215b-143e-230d-bf40ff70985a.png)
ここで `syntax is ok`, `test is successful` にならずに、下図のように `test failed` となったら、エラー内容を確認し、[ドキュメント](http://nginx.org/en/docs/)を参照して、エラーをつぶしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/2eafd02f-a1df-7f26-0c74-df272d772bba.png)
エラーが無くなったら、 `sudo service nginx reload` で設定ファイルをリロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/786e069a-3c0c-1129-e2f8-278fe3153ceb.png)
ブラウザから _http&#58;//YOUR.SERVER.FQDN/_ （ドメイン名は正式なものに置き換えてください）にアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/f77e37f7-67d0-107e-ea66-88d6f88d2b7a.png)
このように、https にリダイレクトされて警告が表示されたら、「詳細設定」をクリックし、「{ドメイン名}に進む」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/b64cb1ef-3d8d-0172-d0fa-ca4bf52d2441.png)
その後、いつもの下図画面が表示されればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/a8876965-3f22-c364-33cc-0a4ca1881270.png)
もし、「リダイレクトされない」「404 Not found になる」などの現象が発生したら、まずは設定ファイルの _YOUR.SERVER.FQDN_ のところが正しいドメイン名(FQDN)になっているか確認してください。とくに英大文字が混じっているとエラーになるようです[^fqdn-error]。

[^fqdn-error]: nginx の設定ファイルの `if` 文の条件のところで使っている `=` 演算子は、大文字・小文字を区別して比較しているためです。`~*` という演算子を使うと、大文字・小文字を無視した正規表現で比較してくれます。 http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if を参照。

# Let's Encrypt からSSLサーバ証明書を取得して本番稼動
「オレオレ証明書」によるテストは通ったので、いよいよ正式なサーバ証明書を取得して Webアプリを全世界に向けて公開してみましょう。

これまではテストだったので、80ポートや443ポートは自分のローカル環境に付与されているグローバルIPだけが通る状態でもよかったのですが、これから説明する「Let's Encrypt」で証明書を取得するためにはすべてのIPからのアクセスを受け付けるようにしておく必要があります[^all-ip]。あらためて、リモートサーバのファイアウォールあるいはセキュリティの設定が、80 と 443 については `0.0.0.0/0` を受け付けるようになっていることを確認しておいてください。

[^all-ip]: 原理的には Lets' Encrypt からのアクセスだけを通せばよいのですが、そんな面倒なことはやってられません。

## Let's Encrypt とは
下図は https://letsencrypt.org/ja/ のトップ画像です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/fc37a316-ae0e-1377-105d-e99bedc7a091.png)
これがすべてを語っています。公式には https://letsencrypt.org/ja/about/ に説明がありますが、

- フリー： 公式なサーバ証明書を無料で署名してくれます。
- 自動化： 後述の certbot というツールで自動的にサーバ証明書を作成し、 nginx などの設定ファイルに登録してくれます。
- オープン： 「自動的な発行・更新のプロトコルを、今後オープンスタンダードとして発行し、誰でも採用できるようにする」らしいです。

すばらしい組織と取り組みだと思います。ただいろいろと制限はあって、たとえば署名の有効期間は3か月となっており、それを超えて使用する場合は、都度、期間の更新処理を行う必要があります。とは言え、この更新処理自体も自動化されているので、たいした制限というわけではありません。

Let' Encrypt の仕組みに興味がある方は、 https://letsencrypt.org/ja/how-it-works/ を参照してください。


## certbot を使って証明書を自動取得
Let's Encrypt は **certbot** というツールを提供しています。certbot は、証明書を発行してもらおうとしているサーバ内で実行することで、Let's Encrypt 側に対するリモートエージェントとして機能します。Let' Encrypt 側は、この certbot から送られてきた FQDN を使って対象ドメインをホストする Webサーバにアクセスします[^acme-challenge]。そして certbot と情報をやり取りすることによって、

[^acme-challenge]: `http://your.server.fqdn/.well-known/acme-challenge/C-JNXCFGg4QsQvTlVjx6V_...` というようなファイルを取得しにきます。このファイルは、certbot によって作成されます。内容は、Let's Encrypt が送ってきたチャレンジ情報にサーバの秘密鍵で署名したものになります。Let's Encrypt側はこのファイルを取得した後、対象サーバの公開鍵を使って署名を検証します。

- DNSに登録されている FQDN が正しくそのサーバに割り当てられていること
- つまり certbot を実行している人がそのドメインの正当な所有者であること

を確認します。なので、あらかじめ DNS にサーバの FQDN を登録しておく必要があるわけですね。

背景がわかったところで、作業に入ります。まず https://certbot.eff.org/ にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/76b90638-4037-cadc-57ed-82298aa7f481.png)
このような画面が表示されるので、

- Software: 利用する Webサーバ（われわれの場合は nginx）
- System: 利用するサーバOS（われわれの場合は Ubuntu 18.04）

を選択します。するとその下に選択した環境でのインストール手順が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/6049a26b-150e-3e4f-5ce8-58500e614715.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/f9797c71-ad4f-293b-c550-4d9e3b530eb5.png)
`snap` を使ってインストールするようです。ではこの手順に従ってやっていきましょう。

### 1. サーバにSSHでログインする
かつ、 sudo 権限を持っている必要があります。sudo についてはすでに nginx のインストールで確認済みですね。

### 2. snapd をインストールする
Ubuntu 18.04 には snap(d) がデフォルトでインストールされています。もし未インストールであれば、「install snapd」の指示に従ってインストールしてください。

### 3. snapd をアップデートする
下記コマンドを実行し、 snapd をアップデートします。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo snap install core; sudo snap refresh core
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/95f25b24-74ec-ff32-c964-9df7c8e524c5.png)
OKのようです。

### 4. 古い Certbot の削除
以前に apt などを使って OSパッケージとしてインストール済みの Certbot があれば、あらかじめそれを削除しておきます。これから snap で導入するバージョンとの競合を避けるためですね。今回は初めてのインストールなので、ここはスキップします。（以前にOSバッケージでインストールしたことがあれば、ここの指示に従って削除しておいてください。）

### 5. Certbot のインストール
下記コマンドを実行し、 certbot をインストールします。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo snap install --classic certbot
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/930b8749-807a-236f-1989-63033d3432ca.png)
OKのようです。

### 6. certbot コマンドの準備
**certbot** コマンドを shell から呼び出せるようにしかるべき場所にシンボリックリンクを張ります。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo ln -s /snap/bin/certbot /usr/bin/certbot
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/7cf28bb4-3d75-de47-848b-b567ebdfc3a8.png)
OKです。

### 7. certbot を実行する
一番重要な箇所に差しかかりました。ここでは、

- 証明書の取得と nginx の設定ファイルへの登録をやる
- 証明書の取得だけをやる

のどちらかを実行します。ここでは前者のほうを実行して、nginx の設定ファイルも書き換えてもらうことにします。

まず、念のため現状の設定ファイル _/etc/nginx/sites-available/default_ を別の名前で保存しておきましょう。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.back
```
そして次のコマンドを実行します。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo certbot --nginx
```
**注**： <font color="grey">この後、設定ファイルの書き換えでいったん失敗するシナリオになっています。もしそれを避けたい場合は、代わりに `sudo certbot certonly --nginx` を実行してください。証明書の取得だけを行うので、その後自分で設定ファイルを修正することになります[^certbot-failure]。</font>

[^certbot-failure]: でも失敗するという経験も大事ですヨネ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/bb29e967-021e-60bf-3f57-2ca4d9a603ad.png)
まず、連絡用のメールアドレスを聞いてきますので、自身のメールアドレスを入力します。これは gmail アドレスでも何でもOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/fe81b07a-174b-8fd7-2f10-d825a44964fb.png)
>「https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf の利用規約をお読みください。ACMEサーバ (https://acme-v02.api.letsencrypt.org/directory) に登録するには、同意が必要です。」

と言っています（by DeepL）。`A`を入力して同意します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/b4a32f16-8d90-8229-a02b-848cd63b84a2.png)
>最初の証明書の発行に成功したら、電子フロンティア財団とメールアドレスを共有していただけますか？
Certbotを開発していますか？ウェブの暗号化、EFFのニュース、キャンペーン、デジタルの自由をサポートする方法についてのメールをお送りしたいと思います。

と言っています（by DeepL）。とくに情報を必要としないのであれば `N` を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/4fda6b5e-614d-3add-4bac-c6f3c3c3c80a.png)
おや、ちょっと様子が変です。設定ファイルに名前が見つからないと言っています。とりあえずドメイン名を入力します。不安が募ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/7bd59e81-3cde-f376-f9e5-567cce2c51d8.png)
失敗してしまいました。。。どうやら、_/etc/nginx/sites-available/default_ の `server` ブロックに明示的に対象ドメイン名を記述しておく必要があるようです。 _/etc/nginx/sites-available/default_ をエディタで開いて、ssl 用の server ブロックのところの `server_name _` を `server_name YOUR.SERVER.FQDN` に修正してください[^default-host]。_YOUR.SERVER.FQDN_ のところは実際のドメイン名に置換します。以下、 diff の出力です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/0a241fdf-c994-049b-1a8f-e208af7f7dbc.png)
気をとりなおして、もう一度実行します。

[^default-host]: 「[6.1 名前によるバーチャルサーバ](http://www2.matsue-ct.ac.jp/home/kanayama/text/nginx/node39.html)」によると、「もしこれらの server_name と Hostフィールドの名前が一致 しなかった場合には、デフォルトのサーバでリクエストは処理されます。 デフォルトのサーバは、最初に定義されているサーバになります。」ということなので、_YOUR.SERVER.FQDN_ 以外のホスト名でアクセスされてもこの server ブロックが参照されることになります。

```text:bash&nbsp;on&nbsp;remote&nbsp;Ubuntu
sudo certbot --nginx
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/eab632ba-769b-a660-759d-5edae2b35019.png)
nginx の設定ファイルに記述したドメイン名が表示されました（ここでは黄色の大文字で表現しています）。設定ファイルに複数のドメイン名が記述されていれば、ここにも複数の候補が表示されるものと思われます。今回は1つしか記述していないので、それだけが候補になっています。その番号`1`を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/3ab84e15-e4e5-78e7-931a-d09925b6d3c0.png)
前回失敗したときに、すでにドメイン名を入力しているので、証明書自体は作成されているようです。作成したばかりなので、当然まだ更新の時期ではありません。「で、どうするよ？」と聞かれているのですが、ここでは `1` を選択[^cert^rate-limit]して作成済みの証明書を nginx の設定ファイルに登録してもらうことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/ef610a03-9e5e-0d58-43ad-0caad50aca4e.png)
**Congratulations! You have successfully enabled** ということで今度は成功したようです。

[^cert^rate-limit]: `2`を選択すると証明書の再作成だけを行うことになるのですが、Let's Encrypt には、ある一定期間は同じFQDNの証明書の再作成ができない、という制限があります。今回は作成したばかりなので、`2`を選択した場合は、何らかの警告が出てそのまま処理が終了すると思われます。

書き換えられた _/etc/nginx/sites-available/default_ を見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/afc9f25d-425f-de84-52c4-2b0cd88be59d.png)
`# managed by Certbot` というのが certbot によって書き換えられた部分となります[^managed-by-certbot]。秘密鍵およびサーバ証明書へのパスが書き換えられています。

[^managed-by-certbot]: certbot はこのコメントを手がかりにしてロールバックすることができるようです。

### 8. nginx のリロード
`sudo nginx -t` で確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/4562ee2c-9e0a-5c6f-981c-970da0ef8c34.png)
OKなので、`sudo service nginx reload` します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/505e1db4-a390-938a-0367-7a701b0384e6.png)

## 最後のテスト
nginx も問題なく設定ファイルをリロードしてくれました。

ブラウザから _http&#58;//YOUR.SERVER.FQDN/_ にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/66d26f27-248c-e8f1-1bf9-f6572f7f2ed0.png)
「セキュリティ保護」などは表示されずに正しく「Chartの世界にようこそ！」の画面が表示されました。つまりインストールした証明書が公的な認証機関によって署名されたものであると認識されたわけです。

前述したように、今回作成した証明書の有効期間は3か月です。期限切れが近づくと、登録したメールアドレスにお知らせが届くかと思います。その場合は、まず `sudo certbot renew --dry-run` でテスト実行してみてください[^renew-dry-run]。これで問題がなければ `sudo certbot renew` を実行して本当に更新します。

[^renew-dry-run]: このコマンドはいつでも実行できます。今実行して試してみても構いません。

今回は証明書の作成と同時に nginx の設定ファイルの修正も行いましたが、設定ファイルの変更部分を見ると、実質的に `ssl_certificate` と `ssl_certificate_key` のパスを修正しているだけのようです。なので、次に certbot を使用する際は証明書の取得だけを行って、 nginx の設定ファイルへの登録は自分でやったほうが失敗する可能性が減ってよいでしょう。

# おわりに
以上で本連載記事を終わります。

今回の記事では Blazor の話はほとんど出てきませんでしたが、いずれは Blazor の WebAssembly 版についても調査したいと思っています。というか、Blazor ってむしろそっちが本命？ある程度まとまったらまた記事にしたいですね。