みなさんこんにちは。UIT Front-end Dev7チームの吉澤です。主にUITが扱う社内サービスやミドルウェアの開発/運用などを行っています。またDev7チームにはLIFF(LINE Front-end Framework)というLINEが使っているフレームワークの開発をしているメンバもいます。他のUITのチームとは一風変わった特徴的なチームです。
このチームの業務の1つにUITが管理しているPrivate npm registryの拡張、関連機能開発/運用があります。実はこれに関連した全社的にnpmが使えなくなるという障害が2021/2/15にLINE社内で発生していました。今回はその障害について話しながら私たちUITが管理しているPrivate npm registryについての話を書きます。
Private npm registryについて
npm(node package manager)に関して改めて説明の必要はない気がしますが、Private npm registryって何だろう?という疑問は浮かぶかもしれません。文字通りnpm, incが管理している公式のregistryとは別に自分たちでnpmのregistryをホスティングしているという話になります。
LINEでは開発で利用している 2つの npm registry が存在します。1つは全社的にpackage repositoryとして利用している Sonatype Nexus(以下Nexus)で提供されるもの、もう1つはこの記事で取り上げるVerdaccioというOSSを用いたPrivate npm registry(以下Verdaccio)です。後者は私たちのチームでホスティングして運用しています。
Private npm registryに関してのメリットとしては次のようなことが挙げられます。
- private packageの管理がしやすくなること
- registry.npmjs.orgへ送ったリクエストのレスポンスをcacheできること
- リバースプロキシを導入することで任意のロジックが仕込めること
それぞれのメリットについて順に解説します。
まず、1に関してですが、公式のnpmでもprivate packageは作成することができます。しかし人数の増減や権限管理など細かいことを考えると自前でPrivate npm registryをホスティングした方が効率的です。特に権限管理に関しては私たちの運用上問題になることが多いです。導入当時Nexusの権限管理の仕組みが私たちが要件として求める水準のものが提供されておらず、新たに別にVerdaccioを私たちがホスティングするようになったという背景があります。Verdaccio導入に関しての経緯は過去にpodcastで話をしております。もしよろしければこちらのリンクから参照ください。
続いて2に関してですが、Private npm registryを使うことで不要なリクエストを公式のregistryであるregistry.npmjs.orgに送る必要がなくなります。Private npm registryが一度取得したpackageをcacheとして持っておくためです。基本的にPrivate npm registryを使うと以下のイメージのようにリクエストが処理されることになります。

最後に3に関してですが、npmクライアントが送っているリクエストを仲介するサーバが作れるので様々な処理を行うことができます。例えばバージョンごとのnpmクライアントの利用率を集計したり、セキュリティのインシデントが発生したパッケージがダウンロードされたか確認したりなどができます。これについては最後の展望の方で、もう少し深く掘り下げて記述できればと思います。
続いて、実際にUITで起きたある事例を紹介します。
事件発生(2021/2/15)までの構成について
npmのオプションにregistryがあります。このオプションは利用するregistryを指定することができます。.npmrcに記述すると以下のような形になります。
# .npmrc
registry=https://your-registry.com
Private npm registryを利用する場合、使いたいregistryのURLをこのオプションに指定することになります。ちなみにデフォルトは公式のnpm registryであるhttps://registry.npmjs.org/になります。
事件発生までLINE社内でPrivate npm registryの利用は必須ではありませんでした。そのためPrivate npm registryを利用しないプロジェクトも数多く存在し、多くのリクエストがLINEからregistry.npmjs.orgに送られていました。
ここで以下の図をご覧ください。

LINE社内のPrivate Cloudから送られるリクエストは設定を変更しない限り外部から見ると1つのIPアドレスになります(上記の図で言うとyyy.yyy.yyy.yyy)。大量のリクエストがこのIPから公式のnpm registryであるregistry.npmjs.orgに送られた結果、害意のあるアドレスとnpmに判断されてbanされてしまいました。このIPアドレスから送られるリクエストが全て403で返却されるようになってしまい、Private Cloud内の大半のVMからpackageのインストールなどが行えなくなりました。このIPアドレスを利用していたサーバは各種CIやアプリケーションサーバなど多岐に渡り、障害が解決するまでの間、npmに依存したリリースやデプロイが停止するという事態に発展しました。
対策について
幸いなことに全社的に利用しているNexusは別のIPアドレスを利用していたためbanの影響は受けていませんでした。そのためbanを解除してもらうまでの暫定対応としてNexusをregistryとして使用してもらうことになりました。
最終的にbanは無事解除してもらえましたが、次また同じ問題が発生しないとは限りません。本格的な対策を練る必要がありました。この問題は大量のリクエストを特定のアドレスから公式のnpm registryに送ったことが原因になります。そこでPrivate npm registryの社内での利用を必須にすることで、不要なリクエストをLINEのPrivate Cloudから発信しないようにしました。上の「Private npm registryについて」の2で述べたようにPrivate npm registryは一度取得したパッケージをcacheとして持ちます。Private npm registryを使わない場合と比較して、はるかにリクエスト数を減らすことができるのです。
2022/1/24現在、以下のような設定で基本的にUIT内ではnpmを利用することが推奨されています。private packageは私たちUITのホスティングしているVerdaccioを使い、それ以外はNexusを利用するといった形です。
# .npmrc
@privte-scope:registry=https://uit-verdaccio-registry-url # ① @private-scopeというscopeのパッケージのみUITのVerdaccioを使用
registry=https://nexus-registry-url # ② それ以外はNexusを使用
処理の流れとしては以下のような形になります。

当初UITの全てのリクエストをVerdaccioで処理するという話も検討されましたが最終的にその案は一旦見送られました。これはVerdaccioが基本的にスケールアウトに対応していないためで、パッケージの状態をverdaccio-db.jsonなどファイルで所持しているためです。
一応Verdaccioにstorageのpluginを開発する口はありますが、残念ながらこのplugin内でデータ更新などの全ての処理が実装できる訳ではありません。更新処理などスケールアウトする際にデータのロックなどを考慮に入れなければならない処理はVerdaccio本体の実装に依存しています。Verdaccio本体は特にロックなどは行わずに処理を行うので、現状Verdaccioはスケールアウトに対応することができない状態にあります。(とても頑張ればできるかもしれませんが、私がソースコードを拝見した限りかなり厳しい実装のようには見えます)。そのため残念ながらVerdaccioで全てのリクエストを処理するという案は見送りになりました。ちなみにまだ調査中のレベルではありますがnpm installなど読み取りのみ複数台構成にすることでスケールアウトをさせるという提案が上がっていて検討を進めています。
今後の展望
上記のような問題の後も、LINE社外も含めてPrivate npm registryに関わる様々な問題が発生しました。個人的に一番印象に残ったのはcoaやrcなどのnpm hijackでした。hijackされたパッケージがダウンロードされ、エンドユーザがセキュリティリスクに晒されることを防ぐ必要がありました。まず対象のバージョンがダウンロードされていないか確認できるようにしました。しかしこれだけでは誰かがhijackが発生したか気づくまでの間、セキュリティのリスクに晒されることになります。
そこで現状、我々はさらなる対応としてpublishされてから数日経過しないとダウンロードできないような改修を入れようと検討しています。npm hijackにより任意コード実行など極めてセキュリティリスクの高い問題が発生した場合、その問題が発覚するまでの期間は基本的に短くなる傾向にあります。数日経過したパッケージのみ利用するようにすればnpm hijackに晒されているパッケージをダウンロードするリスクが減らせるという考えです。勿論、既存のセキュリティイシューが発覚して直ぐに最新版のパッケージをダウンロードしなければならないケースもあります。そのケースに対策するために、利用ユーザが明示的に利用したいパッケージとバージョンを申請することで、数日経過しなくてもダウンロードできるような形にする予定です。
まとめ
この記事ではUIT Front-end Dev7チームの業務の一つであるPrivate npm registryの運用、開発についてまとめてみました。そこまでメジャーな内容ではありませんがセキュリティリスクへの対応や社内でのパッケージの使用状況の調査など幅広いことに応用ができる奥が深いものだと考えています。もし少しでも興味がございましたらカジュアル面談もしておりますので、こちらのJDから是非ともご連絡ください。