NTTドコモR&Dの技術ブログです。

セキュリティのためのプラットフォームエンジニアリング -認知負荷を軽減するDevSecOps in GoogleCloud

TL;DR

NTTドコモ データプラットフォーム部(以下DP部)所属の黒須です。DP部ではデータ活用の民主化を目指して社内向けにstreamlitの開発プラットフォームを独自に実装し、現在streamlitアプリケーション数及び開発者の数は100人以上、利用者は数千人にのぼります。

参考:昨年記事 社員1000人以上が使う、Streamlit in Google Cloudのサーバレスプラットフォームを完全内製してみた

開発者・利用者の増加に伴い、プラットフォームに求められるセキュリティもより重要になってきました。streamlitの場合、アプリ開発者の方が専業でWEBアプリケーションのエンジニアではないことが多いためプラットフォームにセキュリティにかかわる機能実装をする際はできるだけアプリ開発者の皆様の認知負荷を上げない工夫が求められました。

本記事ではプラットフォームエンジニアリング及びDevSecOpsの観点から、このstreamlit開発プラットフォームにおいて認知負荷を抑えつつセキュリティを向上させる工夫や設計をご紹介します。

想定する読者

  • プラットフォームエンジニアリングに興味がある方
  • DevSecOpsに興味がある方

Canary Release of Cloud Workstations

streamlitのアプリ開発者向けのIDE環境としてDP部ではCloud Workstationsというコンテナベースで管理できるクラウド上のサービスを利用しています。Cloud Workstationsを利用することで以下3点を実現しています。

  1. どの開発者にも再現性のある同一、かつセキュアなIDE環境を提供する(コンテナベースの開発環境管理)
  2. 開発環境をVPC内に用意する(VPC限定公開リソースへのアクセスを実現する)
  3. 開発の必須ツール(GitHub CLI等)を事前にインストール済みの環境を提供し、セットアップの負担を下げる

Cloud Workstationsで利用できるベースのコンテナイメージはGoogle Cloudがセキュリティパッチを当てたイメージを常時公開・更新してくれており、その最新イメージを適用することでOSレイヤのセキュリティパッチを自動適用できます。Cloud Workstationsとしては常に最新のイメージを利用することを推奨されているのですが最新のイメージを直ちに全開発者に自動で適用してしまうと開発者が利用するライブラリとの互換性に問題が生じ、突然昨日は動いていたコードがエラーになる事態が発生する可能性がありました。

そこで本プラットフォームでは下図のように、DP部の一部開発メンバーだけに先行してベータ版イメージとして最新のイメージを適用、そのイメージをもとにして5日以上問題なく利用している(監査ログ上起動ログが5日以上ある)ことを検知し、その後にベータイメージを全開発者に適用する公開版イメージに昇格させるというパイプラインを独自実装しています。この仕組みによって一般の開発者には特別な認知負荷をかけずに、常に自動的にセキュアな開発環境を提供することを実現しました。

具体の実装としては、まずベータ版イメージを日次で最新のgoogle提供のコンテナをもとに更新します。ベータ版のWorkStationsは最新化されているコンテナイメージを参照します。そして、ベータ版のWorkStationsを利用しているユーザーのログをloggingより取得、ログルーターでBQ内に保存し、ベータ版イメージの更新されたログと突合することでベータ版イメージが現時点で何日利用されているのかを確認するビューを用意しています。

こうしてできたビューをもとに最新のベータ版イメージが5日以上利用されている場合、公開版イメージに昇格するというパイプラインを実装しています。このパイプラインのおかげで今年はopensslのバージョンがgoogle提供のコンテナ側でアップデートされてpythonのライブラリとの互換性問題が発生した際も、ベータ版の時点でコンテナバージョンの切り戻しを行うことで、全体の開発者向けには対策案を示した後でコンテナイメージのバージョンアップを行うことができました。

DevSecOps with GitHub Actions Reusable Workflows

画像引用元:DevSecOps

本プラットフォームでは、1つのstreamlitアプリ=1つのGitHubレポジトリとし、各レポジトリにおいてdevブランチへcommitすれば自動でdev環境に、mainブランチへPRすれば本番環境に自動デプロイするCI/CDをGitHub Actionsを利用して実装しています。

当初CI/CDのためのGitHub Actionsのワークフローファイルは各アプリケーション側のレポジトリに配置していましたが、その構成ではプラットフォーム側の管理者が追加でセキュリティ上特定の関数を禁止したり、プラットフォーム側都合の変更を全アプリケーションに反映する際は全アプリケーションのレポジトリに変更を依頼したり、プラットフォーム管理者からPRを数十のレポジトリに出す必要がありました。

そこで、今年はアプリのデプロイのために利用するGitHub Actionsのワークフローファイル及びDockerfileをプラットフォームの管理者が管理するレポジトリ(Adminレポジトリ)内で定義し、各アプリのレポジトリからAdminレポジトリ内のワークフローを呼び出す構成に変更しました。
ref:GitHub Actions / Reusable Workflows

この変更により、プラットフォーム運営側がセキュリティ要件に伴う追加ロジックをCI/CDの中に組み込むことが容易になり、かつ、そのロジックを開発者自身は意識せずに済む、アプリの開発者の認知負荷を上げることなく、CI/CDパイプラインの変更・統制ができるようになりました。

また各アプリケーションのレポジトリで任意の設定を許可するパラメーターを所定のjsonファイルに記載してもらうことで、ワークフロー自体の変更は自由に変更できないように統制を取りつつ、アプリケーションのワークロードに合わせたコンピューティングリソースの変更(cpu/mem等)を柔軟にしてもらえるよう工夫しています。

さらに直近ではこの中央管理されたワークフローの中でpythonの静的解析ツールを導入し(Bandit)、コード内にSQLインジェクションをはじめとした脆弱性がないか自動で検知する仕組みを試験導入しています。

ref:Bandit公式サイト

Auditablity of LLM Access

本基盤では従来DWHとしてSnowflakeの接続をサポートしていましたが、今年はLLMのユースケースをサポートすべくLLMとの接続も一機能として提供を始めました。

LLMを単純に利用するだけであればLLMとの接続をサポートするSDKを利用すればよいだけで得に技術的なハードルはありませんが、プラットフォームとして提供する場合課題になるのは、誰が、いつ、どの環境から、どれくらいのトークンを利用しているかの監査性の担保が必要になります。

この点については従来Snowflakeへの接続もそうでしたが、プラットフォーム側からLLMの利用にあたっては接続用の関数を共通ライブラリとして提供し、それを利用してもらうことでアプリ開発者が特にコードの中で明示的にログ出力用のロジックを実装しなくとも、上記のような監査性を担保できるようにしています。

特にLLMへの接続については、監査性を担保しつつ汎用性を持たせるためLangChainというLLMを利用する上では非常に一般的なOSSをベースにして接続機能を共通関数として提供することで、開発者に実装の自由度・機能性を提供しつつ、プラットフォーム側での監査性を担保できるようにしています。

最後に

何らかのプラットフォームを提供する上でセキュリティ向上をするためには、そのプラットフォームの利用者に何らかの不便や認知負荷を発生させざるを得ない場面がどうしても出てきてしまいます。しかし、その認知負荷を最小限に抑えつつセキュリティを向上させるためには、プラットフォームエンジニアリングとDevSecOpsの考え方を取り入れることが有効であると考えています

現時点で利用可能な技術を最大限に利用しつつ、利用者の認知負荷の軽減とプラットフォームのセキュリティどちらかを諦めるのではなく、どうやって両立させるか。それを突き詰めて考えて、これからもより安全で使いやすいプラットフォームを提供していきたいと考えています。

プラットフォームを運用している方々にとって、本記事が少しでも参考になれば幸いです。 なお、本記事の詳細部分については業務委託先メンバーである、辰己さん・兼子さんに執筆協力いただきました。この場を借りてお礼申し上げます。