Node.js の環境を DevContainer に封じ込めよう

Dec 10, 2025

この記事は mstdn.maud.io Advent Calendar 2025 10日目の記事です.

11月24日 postmanzapier など 500 以上のパッケージがサプライチェーン攻撃を受けていたことが報告されました.

Shai Hulud Strikes Again (v2) - Socket

この Shai Hulud というサプライチェーン攻撃は実は9月にも同様に発生しており, @ctrl/tinycolor パッケージなども同様の手口によって攻撃を受けました.

毎週200万回以上ダウンロードされる人気の@ctrl/tinycolorパッケージが高度なサプライチェーン攻撃「Shai-Hulud」によって40以上のNPMパッケージとともに侵害を受けていると発覚 - GIGAZINE

みなさん驚きなんと今年2回目!w

なんとかならんのか?

Node.js のエコシステムは非常に大きいもので,全世界に数百万のパッケージが存在している上に,それらのパッケージは全世界のプロダクトに利用されています.

私個人としては Node.js のエコシステムに関しては 呆れ諦念 の感情が強く,この脆弱なシステムはもはやどうしようもないと思っています.

(というかどうにかなってたら恐らく Deno は産まれてないし1,こんなサプライチェーン攻撃が頻繁には行われないでしょう)

DevContainer のすゝめ

エコシステムが脆弱なら,ローカルを最後の砦として守るしかありません.

私が最近使っているのは DevContainer です.つい最近も PHP をローカルから追い出して気持ちよくなってました2

Post by @m1sk9@mstdn.maud.io
View on Mastodon

DevContainer に求められること

1. Node.js のバージョン管理ができる

Node.js のバージョン管理には mise や asdf などの外部ツールを使うのが一般的ですが,DevContainer の場合は,それらは単一の Docker Image なのでバージョン管理が簡単です. (Renovate にも更新をさせることができる)

2. 全ての開発環境を Docker Container 内で完結できる

DevContainer は起動した Docker Container に VSCode で接続する仕組みなので, DB などそれ以外の環境を DevContainer に含めれれば,ローカルに何もインストールせずに開発が完結します.

3. ローカルに影響を与えない

何と言ってもこれ.

基本ローカルはクリーンにしておきたいので,DevContainer 内に全ての開発環境を閉じ込めれば,ローカルに影響を与えずに済みます.

最小構成で DevContainer を作ってみる

DevContainer を作るにはプロジェクトルートに .devcontainer ディレクトリを作成し,その中に devcontainer.json を作成します.これが DevContainer の設定ファイルになります.

{
  "name": "Node.js 24.11.1 DevContainer",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  }
}

DevContainer に接続する VSCode は Standalone 3 となるので,customizations フィールドで接続先の VSCode で使いたい拡張機能をインストールしたり,設定を追加したりできます.つまり開発環境を他のメンバーと共有でき,なおかつ完全分離ができるわけです

次に Dockerfile を作成します.この Dockerfile で作成する Docker Image を実際にコンテナ内で使うことになります.

FROM node:24.11.1

# 必要なら追加のツールをここでインストールすることもできる
# RUN apt-get update && apt-get install -y ...

これで Node.js v24.11.1 を使う DevContainer の完成です. 起動するには VSCode のコマンドパレットから "Dev Containers: Reopen in Container" を実行します.

イメージの更新や設定変更をしたら "Rebuild and Reopen in Container" を実行しよう

すると Docker Image のビルド, DevContainer のインストールが行われ,VSCode がコンテナに接続します.

パッと見は SSH Remote と同じ

試しに以下のような静的テキストを返す簡易 HTTP サーバーを作成してみます.

import http from 'http';

const server = http.createServer((req, res) => {
    res.writeHead(200, {'content-type': 'text/plain; charset=utf-8'});
    res.end('Hello, World!\n');
})

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running at http://localhost:${PORT}/`);
});

ポートは自動的に Forward されるので,コンテナ内でサーバーを起動すれば,ローカルホストからアクセスできます.

簡単だね!

node_modules も DevContainer に閉じ込めよう

Node.js のプロジェクトでは node_modules ディレクトリが大量に生成され,ローカルにインストールされると非常に邪魔です.(サプライチェーン攻撃にも利用される)

デフォルトの DevContainer の挙動では,ホストのプロジェクトディレクトリがコンテナ内にマウントされるため,node_modules もホストに作成されてしまいます.

node_modules black hole

これを防ぐ方法として Docker の Volue Trick を利用して,node_modules をコンテナ内に閉じ込める方法があります.

  "mounts": [
    "source=project_node_modules,target=/workspace/node_modules,type=volume"
  ]

DevContainer は魔法の杖ではない

ここまで散々 DevContainer のことを持ち上げましたが,DevContainer でもサプライチェーン攻撃を完全に防げるわけではありません. 侵害された npm パッケージをインストールすれば,コンテナ内では攻撃を受けます.

ただ DevContainer は Docker Container の中に開発環境を構築するので,万が一攻撃を受けたとしてもそのコンテナを破棄すればローカルには影響を与えません.

--ignore-scripts オプションを使う

npm や yarn, pnpm にはインストール時に --ignore-scripts オプションを付与することで,パッケージのインストール時に実行されるスクリプトを無効化できます.

npm ci --ignore-scripts
yarn install --ignore-scripts
pnpm install --ignore-scripts

Shai Hulud のような攻撃はインストール時にスクリプトを実行することで行われるため,このオプションを付与することで攻撃を防げる可能性があります.

1

Deno の v2.0 リリース時に公式から公開された PV "Programming Should Be Simple" は npm の脆弱さをいじってるシーンがある.

2

PHP は別に業務でも書いてないしなんなら嫌いな言語の1つだが,大学の講義で仕方なく使わざる終えなかったので DevContainer に封じ込めた.

3

Remote Connection のような挙動をするので,ローカルの VSCode とは別に拡張機能や設定を持てる.

https://m1sk9.dev/posts/feed.xml