😺

Rider on Windows 開発環境構築 (WSLリモート開発編)

2022/11/15に公開約36,700字

§ 概要

JetBrains社のRiderによる ASP.NETの開発環境を作成します。
.NETアプリケーションはWSL上のDockerコンテナで動作させ、Riderからリモート開発を行います。

§ セットアップ手順

◆ お品書き

今回セットアップするものと記事執筆時のバージョンは以下の通りです。

リビジョン
Windows 11 22H2
WSL2 0.70.4
Ubuntu 22.04.01 LTS
JetBrains Rider 2022.3 EAP8
.NET 6 6.0.110
Mono 6.8.0.105
Docker CE 20.10.21

◆ Windowsの準備

OS

systemdに対応したWSLをインストールするため、OSはWindows11 22H2以降である必要があります。

仮想化可能かどうかの確認

以下の手順で、PCが仮想化可能かどうか調べます。

仮想化が無効になっていれば、Windowsを再起動し、PCのUEFIメニューから仮想化を有効にします(PCによりUEFIメニューの呼び出し方や、仮想化の有効方法が異なります)。

既存のWSLの無効化

既存のWSLが有効になっている場合は、Windowsの機能ダイアログを表示し、"Linux用Windowsサブシステム"を無効にします。


◆ WSLインストール

Github上のmicrosoft/WSLページから、0.70.4のAssetsの"Microsoft.WSL_0.70.4.0_x64_ARM64.msixbundle"をダウンロードします。
そして、PowerShellを管理者モードで起動してインストールします。

PowerShell(管理者モード)
> Add-AppxPackage Microsoft.WSL_0.70.4.0_x64_ARM64.msixbundle

◆ Windows Terminal

Ubuuntuをインストールする前に、Windows Terminalをインストールしておきます。


◆ Ubuntu-20.04

WSLコマンドによるインストール

wslのインストールコマンドではUbuntu 22.04 LTSに対応していないため、まず、Ubuntu 20.04 LTSをインストールします。

PowerShell
> wsl --install Ubuntu-20.04

途中、Ubuntuコンソールが起動し、デフォルトで使用するユーザアカウント、パスワードを聞かれるので入力します。ここで入力するUNIXユーザアカウントは、Windowsのユーザアカウントと一致する必要はありません (下記の例では、UNIXユーザアカウントは"scott"にしています)。

Ubuntu
Installing, this may take a few minutes...
Please create a default UNIX user accout. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: scott         # 任意のアカウント名
New password: ****                     # 上記アカウントのパスワード
Retype new password: ****              # 確認のためもう一度

wsl.conf ファイルの設定

WSL用の設定ファイルを作成するために、エディタを起動します。

Ubuntu
$ sudo nano /etc/wsl.conf              # wsl.confファイルを新規作成

編集内容は以下の通りです。なお、hostnameの"dev1.local"はサーバーに付けるホスト名です。任意の名前を付けてください。

/etc/wsl.conf
[boot]
systemd = true

[network]
generateResolvConf = false
generateHosts = false
hostname = dev1.local

[user]
default = scott

各項目の説明です。

  • systemd(true)は、PID:1でsystemdプロセスを起動させるためのオプションです。これにより、systemctlコマンドが動作するようになり、デーモンの管理などが楽になります。
  • generateHosts(false)は、/etc/hostsファイルを起動時に自動更新させないようにします。/etc/hostsファイルは、後で手動で作成します。
  • generateResolvConf(false)は、/etc/resolv.confファイルを起動時に自動更新させないようにします。これは、DNSによる名前解決できない不具合を回避するためのものです。/etc/resolv.confファイルは、後で手動で作成します。
  • hostname(任意のホスト名)は、インストールしたサーバのホスト名を設定します。複数のWSL環境を用意したときなどの使い分けのために使用します。
  • default(管理ユーザアカウント)は、ログイン時のユーザを指定するものです。ログイン時のユーザはレジストリにも書き込まれますが、ファイルのバックアップなどをおこなった際にその情報が失われてしまうため、明示的に指定します。

wsl.confの設定を反映させるために、exitコマンドでいったんUbuntuコンソールを終了します。

Ubuntu
$ exit                                  # Ubuntuから抜ける
logout

Powershellを起動し、WSLを再起動します。

> wsl --shutdown                        # WSLをシャットダウン
> wsl -d Ubuntu-20.04                   # WSLを起動

ホストファイルの作成

/etc/hostsファイルを作成します。

Ubuntu
$ sudo nano /etc/hosts              # hostsファイルを新規作成

内容を以下のように修正します。

/etc/hosts
127.0.0.1       localhost dev1.local

::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe02::1 ip6-allnodes
fe02::2 ip6-allrouters

pingコマンドで自サーバーが応答すれば、設定は問題ありません。

Ubuntu
$ ping dev1.local -c 1
PING dev1.local (127.0.0.1) 56(84) bytes of data.
64 bytes from dev1.local (127.0.0.1): icmp_seq=1 ttl=64 time=0.011 ms

DNS設定ファイルの作成

/etc/resolv.confファイルを作成します。

Ubuntu
$ sudo rm -f /etc/resolv.conf           # 念のため古いものを削除
$ sudo nano /etc/resolv.conf            # 新規作成
/etc/resolv.conf
nameserver 8.8.8.8

google.comへのpingが通れば成功です (結果内のアドレス等は実行環境依存です)。

Ubuntu
$ ping google.com -c 1
PING google.com (172.217.161.78) 56(84) bytes of data.
64 bytes from nrt20s20-in-f14.1e100.net (172.217.175.78): icmp_seq=1 ttl=115 time=34.1 ms

最新状態に更新

Ubuntu 20.04を最新状態にした後、いったん、WSLを終了させます。

Ubuntu
$ sudo apt update
...()...
$ sudo apt full-upgrade -y
...()...
$ exit
Logout
> wsl --shutdown                        # WSLをシャットダウン

◆ ディストリビューション名の変更

プロジェクト毎に作業する環境を切り替えたいケース等を考え、ディストリビューション名の"Ubuntu-20.04"を変更します。

まず、今の"Ubuntu-20.04"ディストリビューションのバックアップを作成します (バックアップ先のパスとファイル名は適当に付けてください)。

PowerShell
> wsl --export "Ubuntu-20.04" D:\backup\backup.tar

ディストリビューションを格納するディレクトリを作成します。下の例では、ユーザーフォルダー直下にwslimagesというディレクトリを作成しています。

PowerShell
> mkdir $Env:USERPROFILE\wslimages

次に、作成したバックアップイメージを読み込みます。wsl --importの各引数の意味は順番に以下の通りです(dev1は例ですので、任意の名前にしてください)。

  • 新しいディストリビューション名 (下記の例ではdev1)
  • ディストリビューションのイメージファイル (下記の例では$Env:USERPROFILE\wslimages\dev1)
  • wsl --exportコマンドで作成したバックアップイメージ
PowerShell
> wsl --import "dev1" $Env:USERPROFILE\wslimages\dev1 D:\backup\backup.tar

wslコマンドでは、起動するディストリビューションを"-d <<ディストリビューション名>>"で指定できます。
しかし、毎回入力するのは面倒なので、"-d"オプションを省略したときのデフォルトのディストリビューションを指定することができます。
以下のコマンドで、デフォルトのディストリビューションを指定します。

PowerShell
> wsl --set-default "dev1"

作成したバックアップファイルはもう不要ですので、削除してもかまいません。

PowerShell
> rm D:\backup\backup.tar

Windows Terminalの設定

Windows Terminalを起動し、タイトルバーのプルダウンメニューから設定を開きます。
スタートアップの既定のプロファイルを先ほど作成したディストリビューションに設定します。
これで、Windows Terminalを起動すると、WSLのUbuntuが起動されるようになります。

この時点で、最初に作成したディストリビューション(Ubuntu-20.04)は、もう不要ですので削除します。

PowerShell
> wsl --unregister "Ubuntu-20.04"

◆ Windows ホストファイル

Windows上でメモ帳などのエディタツールを管理者モードで起動し、C:\Windows\System32\drivers\etc\hosts ファイルを編集し、以下の行を追加します。
なお、"dev1.local"は、実際には、インストール時に設定したホスト名を指定してください。

\Windows\System32\drivers\etc\hosts
127.0.0.1     dev1.local

◆ Ubuntu 22.04にアップグレード

do-release-upgrade

Ubuntuも最新のLTSである22.04にアップグレードします。
まず、Windows Terminalで作成したUbuntuのディストリビューションを起動します。
立ち上がったら、do-release-upgradeコマンドで、OSをアップグレードします。
なお、途中画面が切り替わり、"Restart services during package upgrades without asking?"と聞かれたら"Yes"と答えておきます。

Ubuntuコンソール
$ sudo do-release-upgrade                 # 最新のディストリビューションに更新
...()...
Installing the upgrade can take several hours. Once the download has
finished, the process cannot be canceled.

 Continue [yN]  Details [d] y             # 処理を続行するか聞かれるのでyを入力

Fetching
...()...
Building dependency tree
Reading state information... Done

Searching for obsolete software
Reading state information... Done

Remove obsolete packages?

70 packages are going to be removed.

 Continue [yN] Details [d] y             # 使わないパッケージ消していいか聞かれるのでyを入力
...()...
System upgrade is complete.

Action required

Exit all other instances of Ubuntu WSL before continuing.

Unsaved progress may otherwise be lost.

To continue please press [ENTER]         # ENTERキーを入力

WSL restart required

Exit this instance of Ubuntu WSL.

The upgrade will then be complete.

To continue please press [ENTER]         # ENTERキーを入力
...()...

$ exit                                   # いったんUbuntuから抜ける

ここで、WSLを再起動します。Windows TerminalでPowerShellを開き、以下のコマンドを実行します。

PowerShell
> wsl --shutdown                         # WSLをいったん終了
(終了後、Windows TerminalでUbuntu立ち上げ)

SSH設定

Windows側からWSLにSSHで接続できるように、設定を行います。
まず、接続用の鍵を発行するために、以下のコマンドを実行します。
パスフレーズはローカルホストの接続にしか使用しないので空にします。

Ubuntu
$ ssh-keygen -t ed25519 -C "" -f ~/.ssh/id_dev1
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/scott/.ssh/id_dev1
Your public key has been saved in /home/scott/.ssh/id_dev1.pub
The key fingerprint is:
SHA256:...()...
The key's randomart image is:
...()...

公開鍵をauthorized_keysファイルに追加します。

Ubuntu
$ cat ~/.ssh/id_dev1.pub >> ~/.ssh/authorized_keys

次に、秘密鍵をWindows側にコピーします。作業は、このままWSL側でしてしまいます。Windows側の秘密鍵の名前はなんでも構いませんが、ここではid_dev1.keyにしています。
なお、<<Windowsのユーザ名>>は、この作業を行っているユーザのWindows上のアカウント名です。(つまりは、ユーザーのホームディレクトリ)。

Ubuntu
$ mkdir -p /mnt/c/Users/<<Windowsのユーザ名>>/.ssh
$ cp ~/.ssh/id_dev1 /mnt/c/Users/<<Windowsのユーザ名>>/.ssh/id_dev1.key

試しにPowerShellからssh接続してみます。初めて接続するときはfinger printに関するメッセージが出るので"yes"と答えておきます。

PowerShell
> ssh dev1.local -i $Env:USERPROFILE\.ssh\id_dev1.key -l scott
The authenticity of host 'dev1.local (127.0.0.1)' can't be established.
ED25519 key finger print is SHA256:...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

ログインできれば設定はokです。

起動失敗しているサービス

WSL環境では、systemdのうち起動に失敗している可能性のあるサービスがあります。失敗しているサービスがあるかどうかは、systemctl --failedを実行して確認することができます。

Ubuntu
$ systemctl --failed
  UNIT                       LOAD   ACTIVE SUB    DESCRIPTION
* systemd-remount-fs.service loaded failed failed Remount Root and Kernel File Systems
* systemd-sysusers.service   loaded failed failed Create System Users
  • systemd-remount-fs.servcieが失敗している場合

まずfstabファイルを確認します。

Ubuntu
$ cat /etc/fstab
LABEL=cloudimg-rootfs   /      ext4    defaults            0 0

WSL環境のUbuntuでは、"/"にcloudimg-rootfsというラベルが付けられていないのが原因のエラーです。そこで、このラベルを付けてあげます。

まず、"/"をマウントしているデバイスを調べます。以下の結果の場合、デバイスは"/dev/sdc"です(WSLがインストールされている環境により値は異なります)。

Ubuntu
$ mount | grep ext4
/dev/sdc on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)
...()...

以下のコマンドでラベル名を付けます。

Ubuntu
$ sudo e2label /dev/sdc cloudimg-rootfs
  • systemd-sysusers.serviceが失敗している場合

これは、/usr/lib/systemd/system/systemd-sysusers.service 内のLoadCredentialでWSLでロードされていないモジュールを使用するオプションが指定されているのが原因です。

/usr/lib/systemd/system/systemd-sysusers.service
# Optionally, pick up a root password and shell for the root user from a
# credential passed to the service manager. This is useful for importing this
# data from nspawn's --set-credential= switch.
LoadCredential=passwd.hashed-password.root
LoadCredential=passwd.plaintext-password.root
LoadCredential=passwd.shell.root

直接このファイルを修正するのではなく、以下のコマンドで上書きするオプションを記述します。

Ubuntu
$ sudo systemctl edit systemd-sysusers.service

"Service"と"LoadCredential="の二行を追加します。追加する場所は、必ず"### Anything~"と"### Lines below this~"の間に記述してください。

/etc/systemd/system/systemd-sysusers.service.d/override.conf
### Editing /etc/systemd/system/systemd-sysusers.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
[Service]
LoadCredential=

### Lines below this comment will be discarded

Ubuntuを再起動した後、systemctl --failedが何も返さなければ設定成功です。

Ubuntu
(Ubuntu再起動後)
$ systemctl --failed
  UNIT LOAD ACTIVE SUB DESCRIPTION
0 loaded units listed.

◆ WSLg

WSL上のGUIアプリケーションをWindows上で動作させるための設定を行います。

libcuba.so.1の不具合回避

WSLの不具合を以下の手順で回避します。

まず、コマンドブロンプロトを管理者として実行します。

まず、シンボリックリンクになっているかどうかを確認します。下のようになっているとエラーが発生します。

コマンドプロンプト(管理者)
> C:
> dir \Windows\System32\lxss\lib
2022/10/10  00:00            141,504 libcuda.so
2022/10/10  00:00            141,504 libcuda.so.1
2022/10/10  00:00            141,504 libcuda.so.1.1

エラーの原因であるファイルをシンボリックリンクに変更します。

コマンドプロンプト(管理者)
> C:
> cd \Windows\System32\lxss\lib
> del libcuda.so
> del libcuda.so.1
> mklink libcuda.so libcuda.so.1.1
> mklink libcuda.so.1 libcuda.so.1.1

シンボリックリンクになっているかを確認します。以下のようになっていればokです。

コマンドプロンプト(管理者)
> dir \Windows\System32\lxss\lib
2022/10/10  00:00     <SYMLINK>      libcuda.so [libcuda.so.1.1]
2022/10/10  00:00     <SYMLINK>      libcuda.so.1 [libcuda.so.1.1]
2022/10/10  00:00            141,504 libcuda.so.1.1

Ubuntu側でエラーの原因となるパッケージを再インストールし、WSLを再起動します。

Ubuntu
$ sudo apt reinstall libc-bin
(この後、Ubuntuを再起動)

なお、ビデオドライバーを更新すると、libcuda.soが再インストールされる可能性があり、その場合はこの作業をもう一度行う必要があります。

フォント

Ubuntu側でGUIアプリケーションを起動したときに日本語フォントが表示されないため、Windows側のフォントを使えるように設定します。
/etc/fonts/local.confファイルに、Windows側のフォントを認識するように設定を記述します。

Ubuntu
$ sudo nano /etc/fonts/local.conf
/etc/fonts/local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <dir>/mnt/c/Windows/Fonts</dir>
</fontconfig>

日本語化

日本語言語パックをインストールします。

Ubuntu
$ sudo apt install language-pack-ja -y

日付表示等の表記を日本語にしたい方は、以下のコマンドを実行します(反映には再起動が必要です)。

Ubuntu
$ sudo update-locale LANG=ja_JP.utf8

◆ .NET SDK 6 & Mono

.NET SDK 6

.NET 6をインストールします。Ubuntu 22.04からaptコマンドで簡単にインストールできるようになりました。

Ubuntu
$ sudo apt install dotnet6 -y

Mono

続いて、Monoをインストールします。

Ubuntu
$ sudo apt install mono-complete -y

◆ サーバー証明書

開発サーバーの証明書を作成するためには、中間認証局とルート認証局が必要です。

ルート認証局

開発サーバーの証明書を作成するためには、中間認証局が必要です。その中間認証局を作成するために必要な、ルート認証局をまず作成します。

まず作業用ディレクトリと必要なファイル一式を作成します。

Ubuntu
$ mkdir -p ~/certificates/root
$ cd ~/certificates/root
$ mkdir certs
$ mkdir newcerts
$ mkdir crl
$ mkdir private
$ chmod 700 private
$ touch index.txt
$ echo "01" > serial

ルート認証局用の設定ファイルを作成します。

Ubuntu
$ vi rootca.conf
rootca.cnf
[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = .
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

# For certificate revocation lists.
crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_ext

[ policy_ext ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ext

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName                     = jp
commonName                      = Local Root CA

[ v3_ext ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

ルート認証局用の秘密鍵と証明書署名要求(CSR)を作成します。

Ubuntu
$ openssl req -new -nodes -newkey rsa:2048 -keyout rootca.key -out rootca.csr -subj "/C=JP/CN=Local Root CA" -config rootca.conf

ルート認証局用の自己署名証明書を作成します。

Ubuntu
$ openssl ca -config rootca.conf -batch -extensions v3_ext -out rootca.crt.base -in rootca.csr -selfsign -keyfile rootca.key -days 3650
$ openssl x509 -in rootca.crt.base -out rootca.crt

中間認証局

ルート認証局と同様に、中間認証局の作業用ディレクトリと必要なファイル一式を作成します。

Ubuntu
$ mkdir -p ~/certificates/inter
$ cd ~/certificates/inter
$ mkdir certs
$ mkdir newcerts
$ mkdir crl
$ mkdir private
$ chmod 700 private
$ touch index.txt
$ echo "01" > serial

中間認証用の設定ファイルを作成します。

Ubuntu
$ vi interca.conf
interca.conf
[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = .
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

# For certificate revocation lists.
crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_ext

[ policy_ext ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ext

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
commonName                      = Local Intermediate CA

[ v3_ext ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

中間認証局用の秘密鍵と証明書署名要求(CSR)を作成します。

Ubuntu
$ openssl req -new -nodes -newkey rsa:2048 -keyout interca.key -out interca.csr -subj "/C=JP/CN=Local Intermediate CA" -config interca.conf

中間認証局用の証明書を作成し、ルート証明書で署名します。

Ubuntu
$ openssl ca -config interca.conf -batch -extensions v3_ext -out interca.crt.base -in interca.csr -cert ../root/rootca.crt -keyfile ../root/rootca.key -days 3650
$ openssl x509 -in interca.crt.base -out interca.crt

サーバー証明書

サーバー用に証明書を発行します。
まず作業用ディレクトリを作成します。

Ubuntu
$ mkdir -p ~/certificates/server
$ cd ~/certificates/server
$ mkdir certs
$ mkdir newcerts
$ mkdir crl
$ mkdir private
$ chmod 700 private
$ touch index.txt
$ echo "01" > serial

次にサーバー用証明書の設定ファイルを作成します。

Ubuntu
$ vi dev1.conf
dev1.conf
[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = .
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand
rand_serial       = yes

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_ext
email_in_dn       = no

[ policy_ext ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
prompt              = no
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

req_extensions      = req_ext

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ext


[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName                     = jp
commonName                      = dev1.local

[ req_ext ]
basicConstraints    = critical, CA:FALSE
keyUsage            = critical, keyCertSign, cRLSign, digitalSignature, keyEncipherment
extendedKeyUsage    = critical, serverAuth, clientAuth, codeSigning, emailProtection
subjectAltName      = @alt_names

[ v3_ext ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints       = critical, CA:FALSE
keyUsage               = critical, keyCertSign, cRLSign, digitalSignature, keyEncipherment
extendedKeyUsage       = critical, serverAuth, clientAuth, codeSigning, emailProtection
subjectAltName         = critical, @alt_names
1.3.6.1.4.1.311.84.1.1 = ASN1:UTF8String:Development Certificate
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid, issuer

[ alt_names ]
DNS.1 = dev1.local
DNS.2 = localhost
IP.1 = 127.0.0.1

サーバー用の秘密鍵と証明書署名要求(CSR)を作成します。

Ubuntu
$ openssl req -new -nodes -newkey rsa:2048 -keyout dev1.key -out dev1.csr -config dev1.conf

サーバー用の証明書を作成し、中間証明書で署名します。

Ubuntu
$ openssl ca -config dev1.conf -batch -extensions v3_ext -out dev1.crt.base -in dev1.csr -cert ../inter/interca.crt -keyfile ../inter/interca.key
$ openssl x509 -in dev1.crt.base -out dev1.crt

証明書のWSLへの登録

三種類の証明書をUbuntuに登録し、サーバー証明書が正常に発行できたかどうかを確認します。

Ubuntu
$ sudo cp ~/certificate/root/rootca.crt /usr/local/share/ca-certificates/
$ sudo cp ~/certificate/inter/interca.crt /usr/local/share/ca-certificates/
$ sudo cp ~/certificate/server/dev1.crt /usr/local/share/ca-certificates/
$ sudo update-ca-certificates
$ openssl verify dev1.crt
dev1.crt: OK

証明書の登録 (ASP.NET)

作成された自己署名証明書をASP.NETに登録します。ただし、登録にはPKCS12形式の証明書が必要なのでopensslコマンドで変換します。なお、ここで入力したパスワードは、後々必要になりますので、忘れないようにしてください。

Ubuntu
$ cd ~/certificate/server
$ openssl pkcs12 -export -out dev1.pfx -inkey dev1.key -in dev1.crt
Enter Export Password: <YourPassword>
Verifying - Enter Export Password: <YourPassword>

dotnetコマンドを使って、ASP.NETに証明書を登録します。-pの後は、先ほど入力したパスワードを指定します。

Ubuntu
$ dotnet dev-certs https --clean --import dev1.pfx -p <YourPassword>

Windows側への証明書登録

ルート認証局の証明書をWindows側の適当なディレクトリにコピーします (下の例の"/mnt/c/Users/hogehoge"は、C:ドライブの"\Users\hogehoge"ディレクトリを指します。ディレクトリ名は適当なディレクトリを指定してください)。

Ubuntu
$ sudo cp ~/certificate/root/rootca.crt /mnt/c/Users/hogehoge/

次に、ユーザー証明書の管理コントロールパネルを開きます (Windowsバーの検索欄に証明書と入力すれば出てきます)。

信頼されたルート証明機関を右クリックし、すべてのタスクからインポートを選びます。

証明書のインポートウィザードが起動するので、"次へ"をクリックします。

インポートするファイルとしてルート証明書(rootca.crt)を選択し、"次へ"をクリックします。

証明書の配置場所を"信頼されたルート証明機関"にし、"次へ"をクリックします。

確認画面が出て"完了"をクリックすると、セキュリティ警告が表示されますが、そのまま"はい"をクリックします。

Firefox

これでFirefoxで開発サーバー(dev1.local)にhttpsでアクセスしてもエラーを出しません。

Google-Chrome

Google Chromeの場合、dev1.local にアクセスすると、以下の警告画面が表示されます。

このページが表示されているときに、"thisisunsafe"とキーボードから入力すると、次回以降、dev1.local に対して警告画面を表示させないでアクセスできるようになります (ただし、アドレスバーのところには"保護されていない通信"という警告はでます)。

証明書の有効期限

サーバー証明書の有効期限を365日に設定しているので、有効期限間近になってきたら、サーバー証明書以下の手順を繰り返してください。
なお、ルート認証局、中間認証局の有効期限は10年で、それまでは同じルート証明書、中間証明書を使いまわせます。また、別のサーバーの証明書を作成する場合も、同じくルート証明書、中間証明書を使いまわせます


◆ Docker

ビルド用のdockerをインストールします。

GPGキー追加

公式マニュアルにならい、まず、Dockerの公式GPGキーを追加します。

Ubuntu
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

aptリポジトリへの追加

Dockerリポジトリをインストール用ソースに追加します(アーキテクチャ(amd64)、Ubuntuバージョン(jammy)は固定で書いてしまっています)。

Ubuntu
$ echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list

aptコマンドでDocker Engineをインストールします。

Ubuntu
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y

インストール確認

Docker Engineが正常にインストールできたかどうか、以下のコマンドで確認できます。

Ubuntu
$ sudo docker run hello-world
...()...
Hello from Docker!
This message shows that your installation appears to be working correctly.
...()...

dockerコマンドをsudo無しで使用できるようにする

sudoを使わないでもDockerにアクセスできるように、管理ユーザをdockerグループに追加します。
その後、変更を反映させるために、WSLを再起動します。

Ubuntu
$ sudo usermod -aG docker $USER
(この後、Ubuntuを再起動)

containerdのconfigのversionを2にする

containerdのconfigについて、version=1の記述方法はdeprecatedになっていますので、version=2に変更します。

Ubuntu
$ sudo nano /etc/containerd/config.toml

disabled_plugins = ["cri"]をコメントアウトし、その上にversionと新しいdisabled_pluginsの二行追加します。

/etc/containerd/config.toml
...()...
version = 2

disabled_plugins = ["io.containerd.grpc.v1.cri"]
#disabled_plugins = ["cri"]
...()...

◆ JetBrains Rider

インストール

まず、JetBrains Toolboxをインストールします。

インストール後、Toolboxを起動し、入社可能内にあるRiderのメニューから入手可能バージョンを選択します。

バージョン内から、"2022.3 EAP8"を選びインストールします。

インストール完了後、"Install JetBrains ETW Host Service"をインストールするかどうか聞かれますので、"Apply"をクリックします。

起動

ToolboxからRiderを起動します。初回起動時にUSER AGREEMENTが表示されますので、"Continue"をクリックします。

また、JetBrainsのライセンスがあればライセンスの登録を行ってください。

プロジェクト用ディレクトリの作成

Riderを起動した際、Windows側にプロジェクト用ディレクトリが自動的に作成されます。WSL側にも同様のディレクトリを作成しておきます。

Ubuntu
$ mkdir -p ~/RiderProjects

§ サンプルプロジェクト

◆ サンプル1準備

新規ソリューション作成

サンプルとして、ASP.NETのWebAPIアプリを作成します。
Riderを起動し、New Solutionを選択し、.NET > ASP.NET Core Web Applicationを選択し、以下の内容でプロジェクトを作成します。

項目名
Solution name sample1
Project name sample1
Solution directory C:\Users<Windowsのアカウント>\RiderProjects
SDK 6.0
Type Web API
Language C#
Auth No authentication
Docker Support Disabled

このテンプレートは、API (/weatherforecast) を呼び出すと、5件の日付、気温、天気を示すJsonを返すというサンプルが自動的に生成されます。

このプロジェクトを作成したら、"Close Solution"を選んで、プロジェクトを閉じます(File > Close Solution)。

WindowsからWSL側にソリューションをコピー

Windowsのホームディレクトリ下のRiderProjects下に作成されたプロジェクトを丸ごと、WSL側にコピーします。
Windowsのエクスプローラ等を使ってもいいですし、WSL側のコマンドでコピーしても構いません。以下は、コマンドでコピーする例です。

Ubuntu
$ cp -r /mnt/c/Users/<Windowsのアカウント>/RiderProjects/sample1 ~/RiderProjects

Remote Development (WSL)

WSL側のソリューションを読み込むために、RiderのRemote DevelopmentのWSLを選択し、New Connectionをクリックします。

接続先のWSLのディストリビューション (dev1) を選択し、Nextをクリックします。

IDE versionには、Windows側のRiderと同じものが表示されているはずです。
そして、ソリューションファイルを選択するために、Solution file右の"..."をクリックします。

ダイアログは、Windowsのディレクトリで表示されますので、先ほどコピーした先のsample1.slnファイルをUNCパスで指定します (\wsl$\dev1\home\scott\RiderProjects\sample1\sample1.sln)。

初回起動時に、WSL側のリモート開発用のRiderが自動的にダウンロードされるため、少々時間がかかります。

また、起動されるのは厳密にはRiderではなくリモート開発用のクライアントなので、Rider側で設定した内容は引き継がれません。

◆ Kestrel

ASP.NET Coreでは、デフォルトで内部のWeb Server (Kestrel) が有効になっており、そのまま実行するだけでWebServerが起動します。
なお、もう一つのデフォルトで設定されているIIS ExpressはWSL上では動作しません。

launchSettings

デフォルトの設定のままでも動作しますが、localhostに対するドメイン(dev1.local)を設定していますので、localhostを書き換えることにします。

プロジェクト内のProperties/launchSettings.jsonを以下のように編集します。なお、ポート番号はプロジェクト生成時に適当に振られますので、それに合わせてください。

  • IIS Express関連の記述を削除
  • localhostを別名のdev1.localに変更
  • ASPNETCORE_URLSの設定を追加 (IPv4でbindするように指定。ポート番号はapplicationUrlと揃えてください)
Properties/launchSettings.json
{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "sample1": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "https://dev1.local:7045;http://dev1.local:5191",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_URLS": "https://0.0.0.0:7045;http://0.0.0.0:5191"
      }
    }
  }
}

実行とデバグ

まず、テストのためControllerのところにブレークポイントを設定します。
Controllers/WeatherForecastController.csを開き、Get()メソッドにブレークポイントを設定します。

そして、デバグ開始ボタンを押すと、デバグ実行が開始されます。

実行されると、リモート環境(WSL)へアクセスをするためのブラウザを起動する旨を尋ねるダイアログが表示されるので、接続を行います。

ブラウザには、セキュリティ上の警告が出ます (下の画像はGoogle Chromeのもの)。詳細情報を押して、"dev1.localにアクセスする"をクリックしてください。

SwaggerのUIが表示されたら、"GET" > "Try it" > "Execute"の順にクリックします。
すると、先ほど指定したブレークポイントで停止するはずです (左側のResume Programを押すと実行が再開されます)。

これで、WSL上で動作する.NETアプリケーションを、Windows側でデバグすることができました。

◆ Docker (Dockerfile)

このセクションでは、dockerコンテナで.NETアプリケーションを起動してみます。

証明書コピー

ホストとなるUbuntu側で使用した証明書をプロジェクト下にコピーします。

Ubuntu
$ cp ~/certificates/server/dev1.pfx ~/RiderProjects/sample1

.dockerignore

ソリューションディレクトリに".dockerignore"ファイルを作成します。

"sample1"ソリューション(ソリューションウィンドウの一番上の"sample1")を右クリックして、"Add" > "File"を選択します。

ファイル名は、".dockerignore"にします。

.dockerignoreファイルの内容は、以下の通りです。

.dockerignore
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

Dockerfile

"sample1"ソリューションフォルダにDockerfileを追加します。
"sample1"ソリューションフォルダ(ソリューションウィンドウの一階層下の"sample1")を右クリックして、"Add" > "File"を選択します。

ファイル名は"Dockerfile"にします。

以下、Dockerfileの内容です。

Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["sample1/sample1.csproj", "sample1/"]
COPY ["dev1.pfx", "sample1/"]
RUN dotnet restore "sample1/sample1.csproj"
COPY . .
WORKDIR "/src/sample1"
RUN dotnet dev-certs https --clean --import dev1.pfx -p hogehoge
RUN dotnet build "sample1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "sample1.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT "Development"
ENV ASPNETCORE_URLS "https://0.0.0.0:443;http://0.0.0.0:80"
COPY --from=publish /root/.dotnet/corefx/cryptography/x509stores/my/* /root/.dotnet/corefx/cryptography/x509stores/my/
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "sample1.dll"]

実行構成

Dockerfile用の実行構成はデフォルトでは用意されませんので、新規に追加します。
実行メニューからEdit configuration...を選択します。

左上端の"+"をクリックし、Docker > Dockerfileの構成を追加します。
まず、Build横のModify optionsのContext folderを有効にします。

そして、Context folderにプロジェクトのルートディレクトリ(/home/scott/RiderProjects/sample1)を指定します (Context folderには"."と表示されます)。

そして、以下のように値を設定します。

Field Value Memo
Name Sample1 Docker 構成メニュー一覧に表示される名前です
Dockerfile sample1/Dockerfile
Image tag sample1 docker imagesコマンドを実行したときに分かりやすくなります
Container name aspnet_sample1 docker psコマンドを実行したときに分かりやすくなります

そして、Run横のModifyメニューからBind portsを選び、以下のポートを入力します。なお、Host port側の値は下の例では5000, 5001になっていますが、別に、1024以上の任意の値ならなんでも構いません。

Host port Container port Protocol
5000 80 tcp
5001 443 tcp

最終的には、以下のような設定になります。

ビルドと実行

構成を今回作成した実行構成("Sample1 Docker")にしてデバグボタンを押すと、ビルドと実行が開始されます。

アクセス先は、"https://dev1.local:5001/swagger/index.html" になります。
なお、Kestrel環境と同様にブレークポイントを設定するなどのデバグを行うことも可能です。

WSL側からの確認

dockerコマンドを使用することで、コンテナの動作を確認することもできます。

まず、docker imagesで、sample1イメージがビルドされていることが確認できます。

Ubuntu
$ docker images
REPOSITORY                        TAG       IMAGE ID       CREATED         SIZE
sample1                           latest    26ae20867dff   3 minutes ago   212MB
mcr.microsoft.com/dotnet/sdk      6.0       0ff04a23cb9e   2 days ago      737MB
mcr.microsoft.com/dotnet/aspnet   6.0       33a9c85ed2f6   2 days ago      208MB
s

また、docker psで、コンテナが起動していることが確認できます (デバグ中に試してください)。

Ubuntu
$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS      NAMES
34570a50a11c   26ae20867dff   "/riderDebugger/linu…"   4 minutes ago   Up 4 minutes   0.0.0.0:5000->80/tcp, :::5000->80/tcp, 0.0.0.0:5001->443/tcp, :::5001->443/tcp, 0.0.0.0:57000->57100/tcp, :::57000->57100/tcp, 0.0.0.0:57200->57300/tcp, :::57200->57300/tcp   aspnet_sample1

◆ Docker (docker compose)

上記のセクションで動作したコンテナをdocker composeで動かしてみます。

Docker Compose V2の使用

まず、docker composeをdocker内のコマンドで動作させるために、Dockerの設定を変更します。

"Settings" > "Build, Execution, Deployment" > "Docker" > "Tools"の"Use Compose V2"にチェックを入れて、"OK"を推します。

docker-compose.ymlファイル

ソリューションディレクトリに"docker-compose.yml"ファイルを作成します (上記の.dockerignoreと同じ場所)。

docker-compose.yml
services:
  sample1webapi:
    image: ${DOCKER_REGISTRY-}sample1webapi
    build:
      context: .
      dockerfile: sample1/Dockerfile
    hostname: sample1
    networks:
      - private_net
    ports:
      - "5000:80"
      - "5001:443"
      
networks:
  private_net:

実行構成

Dockerfileと同様に、実行構成を追加します。
左上端の"+"をクリックし、Docker > Docker-composeの構成を追加します。
そして、Compose-filesに、追加したdocker-compose.ymlを指定します。

実行

構今回作成した実行構成にして実行ボタンを押すと、ビルドと実行が開始されます。

Discussion

ログインするとコメントできます