wsl2でmkcertを使い、開発環境でhttps通信を警告なしに行う
概要
web関連の開発をwindowsで行う場合は、wsl2内にサーバーを立ち上げて、windows上で動いているchromeなどのブラウザからviewを確認することが多いと思われます。この際に、wsl2側のサーバーでhttps通信、ホストベースのルーティングが含まれている場合の開発環境の立ち上げ方を紹介します。サーバーとしてnode.jsのhttpsライブラリを利用する例で解説します。
環境
今回紹介する環境は以下のようになります:
- node.js v20.10.0
- windows11
- WSL バージョン: 2.2.4.0
- カーネル バージョン: 5.15.153.1-2
- chrome (firefoxではうまくいかないかもしれません。)
mkcertの意義
通信の偽装問題について
https通信はhttp通信を暗号化することにより、通信の漏洩や改ざんを防ぐ仕組みです。しかしクライアント(ブラウザやOS)からホスト(サーバー)に接続するときに攻撃者により通信経路を偽装されて、攻撃者の手元につながってしまうと、通信の暗号化の意味が失われてしまいます。
この問題を解決するにはクライアントが接続先が正当なものかを確認する仕組みが必要です。そのために認証局(Certificate Authority, CA)という第三者機関を介在させます。ホスト側はCAに自身が正当なドメインの保持者であることを証明し、それが確認されたことを示す"署名"を得ておきます。一方でクライアント側は信頼すると決めたCAから証明書をインストールするようにします。この状態においてクライアントはホストに接続します。このときホストから送られてくる証明書に自身が信頼するとしたCAの署名があるかないかを確認することによりホストが正当なものであるかを確認することができます。
ざっくばらんな説明をすると、webサイトの利用者(クライエント)があらかじめ信頼できる第三者(CA)を指定しておき、その信頼できる第三者が利用者に代わり、サイトのアクセス先(ドメイン)とそのドメインがさすサーバーの所有者が同一であることを保証することで通信の偽装問題を解決するというわけです。
mkcertの役割
近年ではブラウザ側のセキュリティー対策が強固になり、ブラウザ側にインストールされていないCAの署名の証明書やそもそもCAの署名がない証明書を使ってhttps通信をしようとする場合は、ブラウザは強めの警告を出したり、場合によっては通信を遮断するようになっています。これは一般のユーザーにとっては非常に有益なことですが、開発者にとっては障害になりうるといえます。
たとえば開発環境でhttps通信を実現するには以下のコマンドで自己証明書をサーバー側で発行する方法が考えられますが、これではブラウザ側が信頼しているCAの署名がされていないので、警告が出ます。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out selfsigned.crt
CAによる署名がない証明書でのhttps通信の際の警告(のちに説明するコードを利用)
https通信の議論に戻ると、ローカルの開発環境のhttps通信で警告が出る要因は、サーバーからの証明書にCAの署名がされていないことといえます。そこで開発環境内でCAを立てて、サーバーの証明書にローカルCAの署名をつけ、そのCAの証明書をブラウザにインストールしてしまえば、ブラウザの警告を回避することができることになるでしょう。これは手動でopensslのコマンドをたたくことで実現することができるのですが、すこし不便です。(ChatGPTに聞けば教えてくれるかと思います。)
このすこし不便なhttps通信に関する証明書周りの管理を担ってくれるのがmkcertというアプリケーションです。mkcertは仮想的にCAを立て、ブラウザに自動的にその証明書をインストールするとともに、サーバー側にはドメインを保証する証明書の署名をおこなってくれます。この証明書を利用して開発環境のブラウザと通信することで、警告のないスムーズな開発ができます。
mkcertのインストール
一般的には詳しいインストール方法は公式サイトといえるでしょう:
ですが、トップページにはサーバーはwsl2でブラウザはwindowsを使うような開発環境の説明がありませんでした。なので一応インストール方法を記載いたします。
mkcertのうれしい機能はブラウザに自動で証明書をインストールしてくれるところだと思うので、windows側でインストールし、wsl側でwindowsのコマンドを動かし、証明書を手に入れるように設定するのが楽な方法でしょう。
chocolateyのinstall
まずはchocolateyをinstallします。[1]管理者権限付きで開いたpowershellに以下のコマンドを入力します:
Get-ExecutionPolicy
このコマンドの結果、Restricted
の場合、
Set-ExecutionPolicy Bypass -Scope Process
を入力し、このプロセス内でのセキュリティポリシーを緩めます。そして、以下のコマンドでchocolateyをインストールします:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
mkcertのinstallとwslからの利用
choco -?
などでinstallを確認できたら、
choco install mkcert
にて、mkcertをinstallできます。mkcert --version
などでinstallの確認を確かめることができます。続いてこのコマンドがwsl側で利用できるように設定します。wslのshellを開き、以下のコマンドを入力しましょう:
sudo ln -s /usr/local/bin/mkcert /mnt/c/ProgramData/chocolatey/bin/mkcert.exe
これで、wslでmkcertコマンドを実行するとwindows側にあるmkcertが動いてくれるようになります!
mkcertの基本的な使い方
ここでmkcertの基本的なコマンドを2つ紹介します:
mkcert -install
このコマンドで、CA証明書が作られて、システムにinstallされます。この際に警告のwindowが立ち上がりますが、okをクリックし証明書のinstallを完了させましょう。
$ mkcert 必要なドメイン名
このコマンドで必要なドメイン名
でのローカルCAの署名付きの証明書が発行されます。
具体例
確認として、シンプルなhttpsサーバーを立ててみることにします。ここではドメインはt.jp
ということにしましょう。2024年現時点でt.jp
は利用されていないためわかりやすいでしょう: https://t.jp
まずCA証明書をインストールします:
mkcert -install
次にmkcertでt.jp
に対する証明書を発行します:
mkcert t.jp
const https = require('https');
const fs = require('fs');
const options = {
cert: fs.readFileSync(`${__dirname}/t.jp.pem`),
key: fs.readFileSync(`${__dirname}/t.jp-key.pem`)
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end(`HTTPS using ${req.headers.host}\n`);
}).listen(8443); // 管理者権限なしでhttps通信の例を示したいので、8443としています。
というコードを以下のディレクトリ構成となるように置きます:
$tree
.
├── cert_server.js
├── t.jp-key.pem
└── t.jp.pem
このディレクトリで、node cert_server.js
を起動させます。別のshellを開き、curlでアクセスを確かめてみます:
curl --insecure https://[::1]:8443 -H "Host:t.jp"
などで、serverが正しく動いていることを確認できるでしょう。次にwindowsのブラウザからt.jp
でアクセスできるようにします。それにはwindowsのC:\windows\system32\drivers\etc\hosts
にドメインを記載する必要があります:
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
から始まるテキストを管理者権限付きのメモ帳などで開き、以下の行を追加します:
::1 t.jp
ここで注意しないといけないのは、127.0.0.1と書かないことです。別の記事で紹介しようと思いますが、2024年時点でのnode.jsのサーバーとwslの仕様の関係によるものです。これを保存するとwindows側のブラウザでhttps://t.jp:8443がアクセスできるようになります。
アクセスしてみると上の画像のようにセキュリティの警告なしに、https通信ができるようになります。
ちなみにWindows側の/etc/hostsの変更はwsl側の/etc/hostsにも反映されるようになります:
curl https://t.jp:8443 --insecure
を入力すると同じ結果が返ってきます。[2] これは便利な機能ですよね。ただwindows側にしかCAの証明書がインストールされていない関係で--insecure
オプションはやはり必要となります。
証明書を削除する実験
ここまでhttpsをブラウザの警告が出ないようにする方法を紹介しました。ではOSから証明書を削除するとどうなるのでしょうか?powershellで
certmgr
を入力してみましょう。すると以下のようなwindowが立ち上がるでしょう:
このwindowsの中の「信頼されたルート証明機関」->「証明書」と進み、発行先がmkcertから始まる行を探します。その行を右クリックし、削除してみましょう。
ブラウザを再度立ち上げ、アクセスしなおすと、
このようにinsecureな通信と認識されてしまうでしょう。これはOSが開発サーバーで利用しているCAの証明書を持っていないため、通信が信頼できないと認識しているからです。mkcert -install
を再度実行することで直すことができます。
最後に
本記事ではmkcertをwindows+wslの開発環境で用い、ローカルでhttpsを警告なしで実現する方法を紹介しました。
実はこの記事を書こうと思った際に/etc/hosts
という文字列が含まれると保存できないという事件に遭遇しまして、しばらく書くのを放置していました。しかしzennの運営団体並びに開発者により、問題が解決されたため本記事を書き終えることができました。このようなプラットフォームを提供・運営していただいていること、本当にありがたく思います。
この記事が皆様の開発体験の向上につながれば非常に幸いです。お読みいただきありがとうございました。
Discussion