今年5月に悪意あるVS Code拡張機能による情報漏えい事件があったのですが、Ubuntu LinuxかLinux Mintを使っている場合は、AppArmorによってVisual Studio Codeに強制アクセス制限をしてしまうのが手軽なようです*1。
- 広範にファイル読み込み、TCP/IP通信は許可
- リポジトリーを置くワーキングディレクトリー(本稿では ~/working がそれだとします)とVS Codeの設定ファイルと一時ファイルのディレクトリー以外は書き込みを許さない
- ユーザーホームの .ssh/ などへの読み込みを禁止する
- ユーザーホーム以外のマルウェアに参照されたくないファイルは、従来からのファイルパーミッションに任せる
方針でVS Codeに強制アクセス制限をかけます。これで、悪意ある拡張が秘密鍵などのクレデンシャル情報を盗んだり、システム領域などにトロイの木馬を仕掛けたりするのを防げます。
ワーキングディレクトリーにマルウェアが仕掛けられ、かつ、それをVS Code以外から実行してしまうと強制アクセス制限の網から逃れられる事になりますが、だいぶリスクは軽減できるでしょう。なお、VS CodeからGitなどでリモートリポジトリーを更新できなくなる弊害があります。
Ubuntu 24のAppArmorの設定ファイルの修正
Ubuntu Linux 24のAppArmorの以下の設定ファイルは初期値で壊れています。
- /etc/apparmor.d/abstractions/pasta
- /etc/apparmor.d/abstractions/passt
mount options=(rw, runbindable) /,
を
mount options=(rw, runbindable) -> /,
と -> を挿入して置き換え、
"" -> "/tmp/",
を
-> "/tmp",
と""を削除してお聞ければ修正できます。
sudo aa-logprofをしていたら、
ERROR: Operation {'runbindable'} cannot have a source. Source = AARE('/')と言うエラーが出で気づいたのですが、最初は何事かと思いました。
VS CodeのApp Armor
Ubuntu Linux 24の初期状態のプロファイル /etc/apparmor.d/code では、VS Codeの実行ファイルである /usr/share/code/bin/code に何の制限もかかっていません。
方針に沿って、以下のように書き換えます。
abi <abi/4.0>,
include <tunables/global>
profile vscode /usr/share/code{/bin,}/code flags=(attach_disconnected) {
# 標準的な設定はインクルードで済ます
include <abstractions/base>
include <abstractions/dbus-session>
include <abstractions/fcitx>
include <abstractions/fonts>
include <abstractions/freedesktop.org>
include <abstractions/lightdm>
include <abstractions/nameservice>
include <abstractions/passt>
include <abstractions/postfix-common>
include <abstractions/user-tmp>
network,
# デバッガーがVS Codeから起動していないプロセスにattachする場合に必要になるが、
# メモリー上のクレデンシャル情報の漏洩や、キーロガー等のマルウェアの混入を許すので、
# 許可しない
# ptrace (read, trace) peer=unconfined,
deny @{HOME}/.ssh/ r,
deny @{HOME}/.ssh/** rw,
deny @{HOME}/Documents/** rw,
deny @{HOME}/Desktop/** rw,
deny @{HOME}/Music/** rw,
deny @{HOME}/Pictures/** rw,
deny @{HOME}/Videos/** rw,
deny @{HOME}/Templates/** rw,
# 標準的な設定(abstractions/*)では入らないがVS Codeの動作に必要
/dev/shm/* rwk,
/proc/** rw,
# /sys/devices/system/** r, # aa-logprofでは /sys/devices/system/node/** の読み込みあり
/usr/** mrix,
/usr/share/code/chrome-sandbox px,
owner @{HOME}/ r,
owner @{HOME}/** mrixk, # ユーザーホームに配置したコンパイラーなどの改ざん防止で書き込みは許可しない
owner @{HOME}/.cache/** mrwixk,
owner @{HOME}/.config/Code/** mrwixk,
owner @{HOME}/.copilot/** mrwixk,
owner @{HOME}/.pki/** rwk,
owner @{HOME}/.vscode-shared/** mrwixk,
owner @{HOME}/.vscode/** mrwixk,
owner @{HOME}/working/** mrwixk,
userns,
}許可は明示的に書かないと禁止になります。
flags=(attach_disconnected)は、コンテナーやサンドボックスが使われた時に、AppArmorがアクセス拒否をしないようにするための設定です。
denyをつけておくと、他の行に関わらず禁止になります。
@{HOME}は /root や /home/uncorrelated を指すマクロです。
パーミッションの種類は、
| m | mmap() による実行可能領域へのメモリマッピングを許可(DLLのロード等に必要) |
| k | ファイルの排他ロック(flock や fcntl)を許可 |
| r | 読み込み |
| w | 書き込み |
| ix | 現在のプロファイルを継承して実行(親と同じ制限) |
| px | 指定の固有プロファイルに遷移して実行(親と異なる制限) |
になります。
usernsは、非特権ユーザーがuser namespaceを作成することを許可する命令で、昔はデフォルトでした。
ファイルを読み込ませたら、AppArmorにこのプロファイルを再読込させます。
sudo apparmor_parser -r /etc/apparmor.d/code
これで、悪意のあるVS Code拡張にだいぶ強くなります。
動作確認
大雑把に変更が反映されているから確認しましょう。~/.ssh/にあるファイルをVS CodeからCTRL + Oをして読もうとすると、

となります。VS Codeの子プロセスからも同様なので、VS Code内のシェルからもアクセスできません。
VS Codeの挙動がおかしいときは
これで問題無いとは思うのですが、プロファイルの制限が強すぎる可能性が高いおかしい挙動に突き当たった場合は、AppArmorの設定ガイドライン通り、flags=(attach_disconnected)をflags=(complain,attach_disconnected)に書き換えてプロファイルを再読込させて、ターミナルでsudo aa-logprofを動かしならがらVS Codeを操作してください。
ただし、新たにインストールした無名な拡張にとってプロファイルの制限が強すぎる場合は、無名な拡張を疑った方がよいかもです。いつも使っている有名な拡張でも、アップデート後に制限に引っかかるようになった場合は、サプライチェーン攻撃で汚染されている可能性もあります。
*1:開発環境全体をDockerコンテナーに入れてしまうことも考えたのですが、VS Codeの更新頻度から煩雑になり過ぎるのでやめました。なお、更新頻度が高くてもユーザーホーム以下のファイルとディレクトリーしか更新しない場合は、そう困るわけではないです。