WSLに置いたdevcontainer.jsonからJetBrains devcontainerを起動する

弊社ではPHPによる開発で使用するIDEはPhpStormが標準となっています。

PhpStormも今どきの流行かdevcontainer対応されており、されているからには使いたいと思うのですが、欲しい機能が無かったり期待した通り動かなかったりということが稀によくあるため、使う側が気を使って工夫してあげる必要があります。

今回はその中の1つ
「WSL上に配置したファイル(プロジェクト)からのdevcontainer起動が正常に行えない」
という問題の回避方法が分かったので共有しておこうという趣旨です。

公式に対して報告されている不具合としてはこれにあたると思われます。
https://youtrack.jetbrains.com/issue/IJPL-65925

そろそろ直りそうな気配もあり、直ったらこの記事も不要になるのですが、それならそれで喜ばしいということで。
(現時点の最新バージョン2025.1では直っていない)

結論から書きますと「devcontainer.jsonでworkspaceMountを指定する」となります。

以下は結論に至る経緯です。

Jetbrains devcontainerでは以下の2通りの方法でdevcontainerを作ることが出来ます。

  • ローカルプロジェクトから作成
    • ホスト機に置かれたファイル(devcontainer.json)を使って起動。
  • VCSプロジェクトから作成
    • VCS(Git)に置かれたファイル(devcontainer.json)を使って起動。
      • 実際の動作としては、使うファイルだけテンポラリディレクトリにcloneされてそこから起動される
    • 起動時にDocker volumeが作成され、そこにIDEが開く対象としてGit cloneされる

ただ、現状「VCSプロジェクトから作成」が以下のような点により使いにくいです。

  • Git cloneした内容がホスト機から直接見られない
    • HTMLやMicrosfot officeのドキュメントを入れてたりすると見られない
  • Git cloneした内容が複数のコンテナで共有できない
    • 専用のランダムな名前のDocker volumeが作成されるので、予めcompose.ymlで指定しておくということが出来ない
    • 静的なファイルだけWebサーバのコンテナで配布するとか、Javascriptは別コンテナのnodeで動かすといったことが出来ない
  • Dockerで使用する設定ファイルをBind mountしにくい
    • devcontainerが使用するファイルだけテンポラリディレクトリにcloneされるのでローカルで一時的に変更を試すといったことが出来ない

であれば「ローカルプロジェクトから作成」を使おうという話になりますが、この時「ローカルプロジェクト」がWSLに置いてあると正常に動作しないという問題にあたるわけです。(Windowsに置いてあれば問題なく動作するが、Dockerのディスクアクセス速度の都合でWSL上に置きたい)

状況を確認するために、以下のファイルを使用します。

\\wsl.localhost\Ubuntu\home\kurosawa\devtest.devcontainer\devtestdevcontainer.json

\\wsl.localhost\Ubuntu\home\kurosawa\devtest.devcontainer\compose.yml

このdevcontainer.jsonを使いdevcontainerを起動すると、IDEからディレクトリの中身(\\wsl.localhost\Ubuntu\home\kurosawa\devtest)が見えません。

Docker inspectでMountsを確認すると以下のようになっています。

Docker単体であればWSLに置いても特に問題なくマウント出来たはずなので確認します。

compose.ymlにvolumesを追加。

docker composeを使って直接起動します。

問題なくディレクトリ(\\wsl.localhost\Ubuntu\home\kurosawa\devtest)の中身が確認出来ます。

Docker inspectでMountsを確認すると以下のようになっています。

これらを見るに以下のような状況と思われます。

  • Dockerはスラッシュ区切りのパスがうまく認識できない
  • Dockerはバックスラッシュ区切りのパスは正常に認識できる
  • devcontainerから起動するとスラッシュ区切りのパスでマウントされる

compose.ymlを上記のまま、devcontainerから起動すればよいのでは?と思いましたが、スラッシュ区切りのパスでマウントされてしまいます。

それならばとcompose.ymlでバックスラッシュ区切りのフルパスを指定してみますが、相対パスで指定した場合と同じでスラッシュ区切りのパスでマウントされます。

では、バックスラッシュ区切りのパスにするにはどうしたらいいかというと、devcontainer.jsonのworkspaceMountで指定します。

compose.ymlのvolumesは不要なので削除します。(書いてもdevcontainer.jsonの設定で上書きされる)

workspaceMountで指定すると、バックスラッシュのパスでマウントされます。

workspaceMountは相対パスで指定しても大丈夫です。
devcontainer.jsonを共有することを考えるとこちらの方がよいでしょう。
マウント先のパスを変更することも出来ます。

devcontainerは指定したcompose.ymlをそのまま使うわけではなく、devcontainerの動作用に改変して使用します。

挙動からの推測になりますがその改変処理時に、compose.ymlのvolumesに書いたパスはdevcontainerが一旦解釈して、結果スラッシュ区切りのパスになりdevcontainer.jsonのworkspaceMountはdevcontainerが解釈せずそのまま使われる。ということなのだと思います。

[LinuC]Linuxのパーミッションについて

2024年4月入社の吉岡です。以前から「Linuxはコードを書くよりも直感的でないし自分にサーバ・インフラ関係はよくわからない…」と苦手意識を持っていたのですが、苦手なまま避け続けるのは精神衛生によくないと感じ取り組んでみることにしました。現在はLinuCレベル1の取得を目指しており、LinuC公式のテキストとPing-tに取り組んでいます。その中で学んだLinuxのファイルやディレクトリのパーミッションについてまとめさせていただきました。

誰向けの記事か:プログラミング諸学者向け

目次

  • パーミッションとは
  • ディレクトリのパーミッション設定例
  • 特殊なパーミッション
  • デフォルトパーミッションの設定

〇パーミッションとは

パーミッションはアクセス権限のことであり、主に用いられるパーミッションとして、読込権限・書込権限・実行権限の3種類があります。各パーミッションを所有者・グループ・その他ユーザごとに設定することでセキュリティを高め、システムの目的に沿った使用がされやすくなります。

各パーミッションで可能になる動作を下記の表にまとめました。

パーミッションファイルの場合ディレクトリの場合
読込権限(r|4)ファイルの内容を表示ファイルのリストを表示
書込権限(w|2)ファイルの上書きファイル作成・削除
実行権限(x|1)ファイルの実行ディレクトリ内に入れる

「$ls -l ファイル名」 または 「$ls -dl ディレクトリ名」 で10桁のパーミッションコードを確認することができます。
パーミッションコードの左から1番目の記号はファイルの種類を表しており、左から2~4番目が所有者のパーミッション、左から5~7番目がグループのパーミッション、左から8~10番目がその他ユーザのパーミッションを表しています。

パーミッションコード例: -rwxr-x–x
ファイルタイプ:「ファイル」
所有者のパーミッション:「読込権限・書込権限・実行権限」
グループのパーミッション:「読込権限・実行権限」
その他ユーザのパーミッション:「実行権限」

ファイルタイプ記号ファイルタイプ
ファイル
dディレクトリ
lシンボリックリンク
cキャラクタデバイス(キーボードなど)
bブロックファイル()
sソケット(双方向)
pFIFO(一方向)
?その他

パーミッションを変更したい場合は、chmod コマンド で8進数またはシンボルで変更後のパーミッションをしてすることができます。

・8進数で指定する場合
付与したいパーミッションを読込権限(4)、書込権限(2)、実行権限(1)とした時の合計値でsi表現する。3桁の8進数で指定する場合は左から所有者のパーミッション・グループのパーミッション・その他ユーザのパーミッションを設定し、4桁の場合は左から特殊パーミッション・所有者のパーミッション・グループのパーミッション・その他ユーザのパーミッションを設定する。

・シンボルで指定する場合
付与したいパーミッションを読込権限(r)、書込権限(w)、実行権限(x)、で表現する。所有者のパーミッション(u),グループのパーミッション(g),その他ユーザのパーミッション(o)に対してそれぞれ設定したいパーミッションを設定する。

〇ディレクトリのパーミッション設定例

個人的にディレクトリのパーミッションは動作を行うことができるのかわかりづらいと感じたので、いつくか具体的な例を挙げさせていただきます。

例1:ディレクトリに読込権限はあるが実行権限がない場合
読込権限があるのでディレクトリ自体の情報は参照できるが、実行権限がないのでディレクトリの中には入れずディレクトリのリストの中身を表示できません。

実行権限を付与したら、リストの中身を取得できます。中身を追加していないのでカレントディレクトリと親ディレクトリのみです。

例2:ディレクトリに読込権限と書込権限はあるが実行権限がない場合
ディレクトリの中に入ることができないので、touch コマンドやリダイレクトを用いてファイルの作成やファイル内容の更新や削除はできない。

*ただしディレクトリ中に入る必要がないので、ディレクトリ名の変更はできる。

実行権限を付与したら、ファイルの作成やファイル内容の更新や削除ができるようになりました。今回は「>」 を用いて文字列の標準出力先を指定ファイルにリダイレクト(宛先ファイルが存在しなければ新規作成/存在すれば上書き)を行い、「>>」 を用いてファイルの中身へ追記を行いました。

例3:権限のないファイルの削除
sample2 ディレクトリ : 読込権限と書込権限と実行権限がある。
sample2/sampleA.txt : 読込権限と書込権限と実行権限がない。
この状態でファイルの削除をしようとすると…削除ができてしまうんです!

グループやその他のユーザでも同様なので、ファイル自体のパーミッションを適切に設定していても、ディレクトリのパーミッション次第でファイルの削除ができてしまいます…
例:
sample2 ディレクトリ :所有者 linuc グループ linuc
sample2/sampleA.txt : 所有者 linuc グループ linuc

適切なパーミッション管理のために、特殊なパーミッションを使用することができます。

〇特殊なパーミッション

・Sticky Bit

設定方法:パーミッション指定で1000番台を指定またはその他ユーザの権限に「t」を付与する。
例:
sample2 ディレクトリ : 読込権限と書込権限があり、実行権限にSticky Bitが設定されている。
sample2/sampleB.txt : 読込権限と書込権限と実行権限がない。
Sticky Bitを設定したディレクトリ内は、root権限ユーザまたは作成したユーザーのみが削除できる。

sudo コマンドを用いればrootユーザでなくても削除することは可能です。
sudo コマンドを使用可能かはユーザが「wheel」グループに所属しているかどうかで確認できます。「wheel」グループに所属していない場合、sudo コマンドを使用できません。

rootユーザで「wheel」グループに所属を追加

sudo コマンドでStick Bitを設定しているディレクトリ内のファイルを削除

パーミッションを設定する際はファイルやディレクトリだけでなく、ユーザのグループにも注意が必要です。

Sticky Bitを設定してあるファイル・ディレクトリを探したい場合
$find / -user root -perm -o=t -type f 2>/dev/null

その他の特殊なパーミッション
・SUID(Set User ID)
プログラムが実行されると、そのファイルの所有者の権限でユーザーが実行されるようにする。実行権限がある場合は「s」、実行権限がない場合は「S」で表記される。
設定方法:パーミッション指定で4000番台を指定またはユーザーの権限に「s」または「S」を付与する。

例:passwd コマンドファイルのSUID
passwd コマンドを用いてパスワードを設定すると /etc/shadow ファイルに変更後のパスワードを書き込みを行います。passwd コマンドを用いる場合、root権限でのみ/etc/passwd ファイルと /etc/shadow ファイルへの書き込みができます。(*ディストリビューションによってパーミッションは異なる場合があるので注意してください)

SUIDを剥奪して passwd コマンドでパスワードを設定しようとすると、/etc/passwd ファイル と /etc/shadow ファイルへの書き込み時にエラーが発生します。(rootユーザまたはsudo コマンドを用いるとエラーは発生しません)

SUIDを付与すると正常にパスワードを設定できるようになりました。

SUIDを設定してあるファイル・ディレクトリを探したい場合
$find / -user root -perm -u=s -type f 2>/dev/null

・SGID(Set Group ID)
ファイルが実行されると所有グループ権限でユーザーが実行できるようにする。
設定方法:パーミッション指定で2000番台を指定またはグループの権限に「s」を付与する。
SGIDを設定したディレクトリ内でファイルを作成すると、作成されたファイルのグループにディレクトリのグループが自動的に設定される。
例:
SGIDが設定されたディレクトリ名: sample3
SGIDが設定されたディレクトリのグループ名: yoshioka
ログインユーザ名: linuc

SGIDを設定してあるファイル・ディレクトリを探したい場合
$find / -user root -perm -g=s -type f 2>/dev/null

〇デフォルトパーミッションの設定

デフォルトパーミッションとはコマンドで明示的にパーミッションを指定せずにファイルやディレクトリを作成した際に付与されるパーミッションのことです。基本パーミッションからマスク値を引くことで設定されます。

・umask コマンド
umask コマンドでマスク値を表示・設定することができます。

基本パーミッションからマスク値を引いた値がファイルやディレクトリ作成時のデフォルトパーミッションです。

ファイルディレクトリ
基本パーミッション666(-rw-rw-rw-.)777(drwxrwxrwx.)
マスク値022022
デフォルトパーミッション644(-rw-r–r–.)755(drwxr-xr-x.)

・デフォルトパーミッションの変更

例1:このシェルセッションでのデフォルトパーミッションを変更したい場合
umask コマンド 実行する
8進数でマスク値で指定する。

シンボルでマスク値を指定する。シンボルは8進数に変換され後に各基本パーミッションから引かれます。

例2:ユーザごとにのデフォルトパーミッションを変更したい場合
変更したいユーザのホームディレクトリ/.bashrc のumask値を変更する。
変更前

変更
$vi ~/.bashrc で個人設定ファイルを開き、umask コマンドを追加して保存し、再度読み込む。

変更後
変更したユーザ(ユーザ名:linuc)のマスク値は0111に変更されているが、変更していないユーザ(ユーザ名:yoshioka)のマスク値は0022のままである。

例:3ユーザ全体のデフォルトパーミッションを変更したい場合
/etc/login.defs のumask値を変更する
変更前
$sudo vi /etc/login.defs

変更

変更後
すべてのユーザーのデフォルトパーミッションが0111に変更されている。(rootユーザのマスク値も変更になっているので使用する際は注意が必要だと思われます)

終わりに

自分がLinuxに苦手意識を持ったきっかけがこのパーミッションだったのですが、取り組んでみるとと理解できる部分が意外と増え苦手意識が少し薄れたと感じています。だれてしまわないように目標を設定して、Linuxやサーバ・インフラ関係のインプットとアウトプットを続けていきます!