TECH PLAY

NTTドコモビジネス

NTTドコモビジネス の技術ブログ

602

この記事は、  NTT Communications Advent Calendar 2024  の記事です。 みなさんこんにちは、イノベーションセンターの冨樫です。Network Analytics for Security 1 (以下、NA4Sec)プロジェクトのメンバーとして活動しています。この記事では「 利用終了したドメイン名の終活に向けて 〜観測環境を作った話〜 」で紹介した観測環境から収集したログの分析結果について説明します。また、この分析の過程で利用終了ドメイン名を管理するにあたり有効なアクションを発見したので、それらについても一部紹介したいと思います。 ログの分析 DNSクエリの分析 Webアクセスログの分析 残存リンクの削除 まとめ ログの分析 本施策では、さまざまな用途で使用していた複数の利用終了ドメイン名に対して、DNSクエリとWebアクセスログを収集しました。ドメイン名の元々の用途によってログの分析結果に傾向がありましたので、一部のマスキングしたドメイン名を使って紹介します。 DNSクエリの分析 7月から11月にかけて収集した約18,000,000件のDNSクエリのタイプを集計しました。下図は、調査対象の全ドメイン名のクエリを合算した集計結果とaaa.example、bbb.exampleにおける集計結果です。 aaa.exampleとbbb.exampleはそれぞれ以下の用途で使用されていた利用終了ドメイン名です。 aaa.example : Webサイトとして利用されていたドメイン名 bbb.example : NTTコミュニケーションズの関連会社のコーポレートドメインとして利用されていたドメイン名 aaa.exampleでは、Aレコードの割合が87%を占めており、全ドメイン名の集計結果におけるAレコードの割合が59%であることを考慮すると、高い傾向にあります。 これはaaa.exampleがWebサイトのドメイン名として使用されていたため、利用終了後の現在においてもWebアクセスを起点とする名前解決が行われていることが原因の可能性があります。さらにここからインターネット上にこの利用終了ドメイン名宛のリンクが残っており、そこからアクセスが発生している可能性も考えられます。 一方、コーポレートドメインであるbbb.exampleはメールアドレスとしても利用されていた背景があり、MXレコードの割合が28%を占めていて、これは他のドメイン名と比較して高い傾向にあります。 また、bbb.exampleのMXレコードに絞ってDNSクエリが来た曜日を集計したところ、下図のように平日のカウントが比較的に高い結果になっていました。これらのことから現在もこのドメイン名を宛先としたビジネスメールや広告が送信されている可能性があると考えられます。(月曜日のMXレコードの件数が土曜日よりも少なかったことは、日本と標準時間が異なる地域からのメールによるものと推察しています。) その場合には、このドメイン名がドロップキャッチされると、それらのメールを第三者が確認することも可能ということになり、ドメイン名の廃止には注意が必要です。 Webアクセスログの分析 Webアクセスログについては6月から11月にかけて約1,700,000件を収集しました。今回はWebアクセスログに含まれるUser-Agentを活用してクライアントの特性を分析しました。 下記は実際に収集したUser-Agentであり、クライアントのOS、デバイス、ブラウザの種類等の情報を得ることができます。また、それらを用いてクライアント端末がPCとモバイルに分類することも可能です。 下図は、クライアントがPCかモバイルかを判別し、全ての利用終了ドメイン名を対象に集計した結果と、ccc.example のみを対象に集計した結果を比較したものです。(Others は分類するための情報がUser-Agentに含まれていなかった場合に該当) ccc.exampleは以下の用途で使用されていた利用終了ドメイン名です。 ccc.example : NTTコミュニケーションズと関連するプロスポーツチームのホームページに利用されていたドメイン名 ccc.exampleのモバイルからのアクセスは18%であり、全ての利用終了ドメイン名を対象に集計した結果と比較すると5倍以上比率が高いという結果でした。これはccc.exampleで運営さていたWebサイトは、スポーツチームのホームページであり、訪問者の嗜好でアクセスが発生している場合も多く、そのためモバイルの比率が高まったと考察しています。 下図は各クライアントが使用したOSの件数を示しています。ccc.exampleではiOSからのアクセスが3位(全体では6位)、Androidからのアクセスが4位(全体では5位)であり、ここからもモバイルのアクセスが多いことを確認できます。 今回User-Agentを使ったように、WebアクセスログにはDNSクエリにない情報が含まれており、これらを活用して利用終了ドメイン名のクライアントの情報を分析可能です。 残存リンクの削除 利用終了ドメイン名を廃止する際には、インターネット上にそのドメイン名宛のリンクが残っていないことが理想的です。これは廃止後にそのドメイン名がドロップキャッチされ、新たな登録者がWebサイトを立ち上げた場合に、そのインターネット上に残ったリンク (以下、残存リンク) から訪問者が意図しない新規のWebサイトに誘導される可能性があるためです。また、それが悪性のWebサイトである場合、そのドメイン名を前に登録していた企業のレピュテーションに影響が生じることも想定されます。以上より、インターネット上の残存リンクはできれば削除することが好ましいです。 そのために、まずは残存リンクが掲載されているWebページを特定する必要があり、今回は収集したWebアクセスログのRefererを利用した調査をしました。 Refererを参照することで、Webサイトの訪問者がどのサイトからアクセスしてきたのかを確認でき、そのサイトには残存リンクが存在すると想定されます。ただし、クライアントのWebブラウザの設定などが影響し、Webサイトの詳細なパスまで入手できないことがほとんどでした。つまり、訪問者が来たWebサイトのドメイン名 (サイト名) は分かるが、どのページから来たのかということまでは分からない状況がほとんどでした。 訪問者が来たWebサイトのページを特定するためにGoogle Dorksを用いた調査をしました。Google Dorksは、演算子を駆使したGoogleでの検索手法のことで、今回は下記の検索演算子を使用しました。 site 演算子 : 指定されたドメイン名でインデックスされているWebページを検索 link 演算子 : 指定URLへのリンクを含むWebページを検索 例えば、Refererから確認できたドメイン名がexample.comで、アクセスされた利用終了ドメイン名がaaa.exampleの場合には下記で検索しました。このようにすることでaaa.exampleへのリンクを含んだexample.comのページを調査できます。 site:example.com link:aaa.example 今回はこの手法で複数の残存リンクが存在するページを特定できました。また、Wikipediaなどの編集が可能なサイトについては自ら削除対応しました。 このように残存リンクをインターネット上から減らすことで、ドメイン名廃止後にドロップキャッチされた際のリスクを軽減できます。 まとめ 今回は「 利用終了したドメイン名の終活に向けて 〜観測環境を作った話〜 」で紹介した観測環境から収集したDNSクエリとWebアクセスログを分析し、各利用終了ドメイン名ごとの特性を評価しました。 また、利用終了ドメイン名を管理する上で有効なアクションとして残存リンクの調査方法を紹介しました。 NA4Secプロジェクトについては、このブログの記事  サイバー脅威インテリジェンス(CTI)配信はじめました  をご覧ください。 ↩
アバター
はじめに こんにちは!NTTコミュニケーションズの 2024年夏の現場受け入れ型インターンシップ に参加させていただきました、大学院1年生の 山本 です。 現在はフィッシングサイトの検出をテーマに研究活動を行っています。 本記事では、インターンシップを通して体験させていただいた業務内容のうち、特にフィッシングキットの分析について紹介させていただきます! はじめに 参加チームについて(NA4Sec) インターンシップに参加した経緯 体験内容の概観 フィッシングキットの詳細分析 構成ファイル 分析方法 騙っているブランドについて どんな情報を窃取しようとしているのか どのような処理があるのか 検知に活⽤できそうな情報 攻撃者に関係していそうな情報 おわりに 参加チームについて(NA4Sec) 私はインターンシップの期間中、 NA4Sec というチームに参加させていただきました。 NA4Secの正式名称は「Network Analytics for Security」であり、 「NTTはインターネットを安心、安全にする社会的責務がある」 ということを理念として掲げ、攻撃インフラの解明や撲滅を目指して活動しているチームです! 1 将来的にセキュリティに携わって人々を守りたいと考えている自分にとって、とても魅力的なチームでした。 インターンシップに参加した経緯 私はフィッシングサイトの検出について研究しているのですが、提案手法の有効性を評価するために本物のフィッシングサイトにアクセスして情報収集をすることがあります。 フィッシングサイトの情報源をX(旧Twitter)やフィッシングメール、SMSなどとしており、フィッシングサイトの情報を収集する際に、 Metemcyberアカウント により発信される情報を閲覧したことがありました。 Metemcyber とは、「セキュリティ運用の健全化を提供することを目標として活動しているプロジェクト」 2 で、 以前のインターンシップ でも研究開発が行われていました。 特にXでは、次の投稿のようなフィッシングサイトに関する情報が発信されています。 🚨⚡ #Phishing #フィッシング詐欺 (🇯🇵) Brand: #税務署 #国税庁 IP: 🌍 43.133.6[.]103 (ASN:AS132203) URL: 🎣 hxxps://amaglobal.cn/ 🎣 hxxps://canchuan.cn/ 🎣 hxxps://exterminator.cn/ 🎣 hxxps://hideandseek.cn/ 🎣 hxxps://netwalk.cn/ H/T to Team NA4Sec pic.twitter.com/YpZPMP3SGN — Metemcyber (@Metemcyber) 2024年10月2日 このように情報発信するために、どのようにフィッシングサイトの情報を収集・精査し、発信しているのか興味があり、インターンシップへの参加を希望しました。 また以前のインターンシップ体験記から、フィッシング詐欺に関する業務について体験できると知ったため、さらに意欲が向上しました! 体験内容の概観 今回のインターンシップでは、大きく分けて4種類の業務に参加させていただきました。 脅威調査(Cobalt Strikeとその悪⽤) 脅威探索(Cobalt Strike C2サーバに関して) 脅威情報配信(フィッシングサイトに関して) フィッシングキット分析 脅威調査、脅威探索、脅威情報配信については今回は割愛し、 「フィッシングキット分析」 について詳細に紹介させていただきます。 3 フィッシングキットの詳細分析 まずフィッシングキットについて簡単に説明すると、 「フィッシングサイトお⼿軽作成ツール」 です。 フィッシングキットを利用することで、攻撃者は簡単にフィッシングサイトを用意できます。 このようなフィッシングキットを分析することで、フィッシングサイトのサーバ側での処理や設定ファイルに記載されている内容、さらに攻撃者に関わる情報(窃取した情報の送信先、コードに残したコメント)を入手できる可能性があります。 今回は実際に使用されていたフィッシングキットについて詳細に分析し、その分析レポートを作成することになりました。 以前のインターンシップ体験記 でもフィッシングキットの分析を扱ったものがありましたが、本体験記ではソースコードレベルでの個別機能把握とそれを踏まえた検知の検討に焦点をあてて紹介します。 今回、分析時の観点として以下について教えていただきました。 騙っているブランドはなにか どんな情報を窃取しようとしているのか どのような処理があるのか 検知に活用できそうな情報 攻撃者に関係していそうな情報 構成ファイル 分析対象のフィッシングキットは、NA4Secチームが過去に収集して保存していたzipファイルのうちの1つです。 今回のインターンシップでは、NA4Secのアナリストとして自身が入手したフィッシングキットについて分析レポート作成を依頼されたというシナリオベースで分析に取り組みました。 このzipファイルを展開すると、下図のようにいくつかのファイルとフォルダが確認できました。 分析方法 VS Code でソースコードを読むことで処理を追っていきました。 以下にその手順や結果を記載します。 騙っているブランドについて まず⾃分は、フィッシングキットに含まれる画像からブランドを推測できるのではと考え、画像を探してみました。 すると、「/assets/img」に画像ファイルが存在し、その⼤半は某大手ECサービスに関連しているようでした。 このため、騙っているブランドは某大手ECサービスであると最初は判断していました。 しかし、発見した画像ファイルがどのように呼び出されているのかは不明でした。 そこで「index.php」がまず呼び出されるのではないかと考え、そこから処理を追ってみると、呼び出されているのは「ap/signin.php」や「_ap/signin.php」でした(今回、「ap」はMobile用、「_ap」はDesktop用に用意されたディレクトリ)。 どちらも確認するとログインページのようであり、ともにtitleタグの値に某交通系サービスの名前が記載されていました。 以上から、今回のフィッシングキットによって作成されるフィッシングサイトが騙るブランドは某交通系サービスであると判断しました。 またフィッシングキットに複数のブランド情報があったことから、フィッシングキットは再利用されていること、画像だけ確認して騙っているブランドを判断するのではなく処理を⼀つずつ追うことが重要であることを学びました。 どんな情報を窃取しようとしているのか phpファイルの処理を確認していくと、以下のような情報などを窃取しようとしていることが分かりました。 登録しているメールアドレス、パスワード 名前 生年月日 携帯電話番号 カード番号 ワンタイムパスワード 入力フォームに上記のような情報などの入力を求める記述が存在したため、phpファイルを読むだけで窃取しようとしている情報が判明しました。 どのような処理があるのか クローキング が行われていることが分かりました! クローキングについて 以前のインターンシップ体験記 を参考に紹介すると、「攻撃者が標的とする対象には悪性の挙動を示し、対象以外には悪性でない挙動を示すようにする処理」です(体験記により詳細に記述されていますので、ぜひご覧ください!)。 自身の収集経験からも、最近のフィッシングサイトではよく行われている処理だと思います。 本フィッシングキットでもクローキング処理がphpファイルで確認できました。 次の図の処理では、$bannedに格納されている⽂字列がアクセスを試みるUser Agentの文字列に含まれている場合、__exit()関数によりフィッシングページを非表示にする処理が行われていました。 また、 ipregistry のAPIが利用されていました。 次の図はAPIを利用するためのURLが作成されている箇所を抜粋したものです。 ipregistryのAPIによりフィッシングサイトへのアクセスを試みるIPアドレスに関する情報が取得され、取得した国情報などによるクローキングが行われていました。 クローキング処理以外にも、 窃取したデータの送信処理 も発見しました。 次の図は、データ送信に利用されたと思われる関数です。 $msg には窃取したデータが格納されており、窃取したデータはTelegramのAPIを利用した送信処理が行われていました。 本フィッシングキットでは、 file_get_contents により API が利用されていました。 以上のような処理の分析により、データ送信処理や、普段フィッシングサイトの情報収集時に自身もふれていたクローキング処理がどのように実装されているか知ることができました。 検知に活⽤できそうな情報 「この情報に注⽬すれば、フィッシングサイトと判断できるのではないか」という情報を探しました。 今回は3つほど紹介します。 敬体と常体の混合 :可能性低 フィッシングサイトで⾒られるおかしな⽇本語表現に着⽬しました。 敬体とは「です」「ます」、常体とは「だ」「である」のような語尾で終わる文体です。 フィッシングサイトの中には上図のように敬体と常体が不自然に混ざった文章が登場することがあります。 しかし、おかしな⽇本語表現は正規サイトでも存在するため、フィッシングサイト検知の根拠としては弱いです。 コピーの痕跡 :可能性高 正規サイトのページを保存した痕跡らしきものがフィッシングキットのコード中に残っていました。 通常、正規サイト(特に正規ログインページ)に他サイトからコピーしてきた痕跡があるとは考えにくいので、検知に活⽤できると判断しました。 urlscanによる調査 :可能性高 インターンシップでは、urlscanによる調査方法について指導していただきました。 urlscan とは、指定したURLのスキャンを実行し、スキャンしたURLが安全かどうかなどの情報を確認できるサイトです。 urlscanによりフィッシングサイトを構成するファイルのハッシュ値を確認すると、同様のハッシュ値を持つ、フィッシングサイトと思われるサイトのスキャン結果を得ることができました。 このことから、既知のフィッシングサイトを構成するファイルのハッシュ値を検知に活用できると判断しました。 ただし、一部のハッシュ値を確認すると、正規サイトのスキャン結果が多く得られることもありました。 この理由として、以下の2点が考えられると教えていただきました。 対象ファイルがフィッシングサイトにも正規サイトにも存在するから クローキングによる正規サイトへのリダイレクトもスキャンされ、正規サイトのファイルも記録されるから そのため、検知にハッシュ値を利⽤する場合には⼀つのリソースのハッシュ値だけでなくできるだけ全てのハッシュ値を確認する必要があると考えます。 urlscanによる調査の詳細ついては、 以前のインターンシップ体験記 で紹介されていますので、ぜひご覧ください! 今回の経験から、urlscanの活用方法について知ることができました。 攻撃者に関係していそうな情報 「Capricorn.ini」ファイルに攻撃者に関係していそうな情報が確認できました。 例として、情報送信に使われたであろうTelegramのChatのID情報や、ipregistryのAPIを利用するためのkey情報などが存在しました。 このように、フィッシングキットを確認するだけで攻撃者が利用したサービスの情報についても知ることができました。 おわりに 本インターンシップのカリキュラムで、私は初めてフィッシングキットの分析に挑戦することになりました。 一つ一つの処理を地道に追っていく作業だったので時間がかかりましたが、とても楽しく有意義な経験となりました! 具体的には、フィッシングキット分析関連の業務に多く時間を割いてくださったことで長時間分析に挑戦でき、詳しく知らなかった外部APIサービスについて詳細に調査でき、またメンターの益本さんから知識共有もしていただきました。 加えて分からない箇所についてはアドバイスをいただき(例:urlscanによる調査)、そのアドバイスをもとに調査を行えたことなどから、自身の研究分野であるフィッシングサイトについて知らなかった挙動や検出方法について学ぶことができました。 さらに分析により普段収集していたフィッシングサイトの挙動(例:クローキング)を裏付けることができ、User AgentやIPアドレスを工夫するなど今後どうフィッシングサイトに対応するかについて知見を広げる機会となりました。 今回のインターンシップで得た知識を自身の研究活動の一環であるフィッシングサイトの情報収集に活かしたいです。 上記で紹介した業務以外にも、対面とリモートの両方での業務体験や、NA4Secチームが関わるイベントにも多数参加させていただき、現場の雰囲気も充分に理解できました。 興味のあったフィッシングサイトに関する業務(特にフィッシングサイトの情報収集・精査方法)についても座学や業務体験を通して経験できました。 先述したフィッシング詐欺に関する学びはもちろん、昼休みや夜に行われるイベントでも技術的知識を共有し、チームメンバーの成長を促そうとする現場の上昇志向はぜひとも真似すべきものだと思いました。 現在の自分は研究室に所属しているので、今後研究室メンバーへ色々な知識共有を行うことで、上昇志向的な姿勢を身に着けたいです。 このように、技術面ではフィッシングサイトに関する多くの知識を得たこと、非技術面では現場の積極的な姿勢を学ぶことができたことから、インターンシップへの参加目的を達成できたと思っています。 本当にありがとうございました! 最後に、自分のインターンシップに関わってくださった皆さま、特に上長の神田さん、メンターの益本さん、色々なお世話をしてくださった鮫島さんには本当に感謝しています。 繰り返しになりますが、ありがとうございました! NA4Secに関する記述は「 BSides登壇のBサイド ~なんで、私が海外セキュリティカンファレンスに!?~ 」という記事を参考にしました。最近のNA4Secに関する活動も執筆されていますので、ぜひご覧ください! ↩ 引用元は「 日本を狙ったフィッシングサイトの情報配信はじめました 」という記事です。XでのMetemcyberアカウントによるフィッシングサイトの情報発信について紹介されています。 ↩ 脅威調査・脅威探索については、 以前のインターンシップ体験記 が参考になると思います!ぜひご覧ください。 ↩
アバター
NTTコミュニケーションズ(以下、NTT Com) イノベーションセンター(以下、IC)の加藤( @katomasa23 )です。 個人的にちょうど良いタイミングということもあり、新卒でエンジニアを始めて約3年半を振り返り、考えたことをまとめたいと思います。タイトル的に退職エントリみたいですが、退職しません、というお断りだけ最初に入れておきます。 自己紹介 私のキャリアの話を書いていくので、最初に背景情報として自己紹介させてください。 2021年4月にNTT Comに新卒入社 大学は文系学部(商学部)出身 現在のメイン業務はローカル5GやWi-Fiなど各種無線技術を用いたユースケースの開拓 この記事が、文系・新卒でエンジニアというキャリアを考えている就活生や、NTT Com含め、IT系企業に就職が決まり、希望部署や職種に悩んでいる方の参考になれば幸いです。 文系出身でなぜエンジニアへ? 理由を書くだけで1つの記事として完結できそうなテーマではありますが、なるべく簡単に振り返ってみます。 NTT Comインターンシップへの参加 私は就職活動の頃、幅広い業界(IT・通信・金融・商社・マスコミ)を見ていました。その中でNTT Comに関心を持ったのは、経理の専門職エントリーができるからでした。先述した通り、私は商学部出身で、簿記2級も取得していたことから、経理の仕事に興味がありました。そこでNTT Comのインターンシップに第1希望経理・第2希望営業で応募しました。 インターンシップでは第1希望の経理ではなく、第2希望の営業の仕事を経験しました。そこではICTでお客さまの可能性を引き出せるのではないか?というやりがいに惹かれ、非常にワクワクしたのを覚えています。 また、インターンシップ中にIOWN構想についても聞き、「 日本からゲームチェンジを起こしたい! 」という思いを持って本選考への応募を決意しました。 入社までの想い 採用が決まり、2月の内定者面談で初期配属の希望を聞かれました。当時は、IOWNを広める、という思いに一番近そうなのはマーケティングかなと思っていました。ただ、マーケティングをやる前に技術的な凄さやアピールポイントを自分なりに腹落ちさせておきたいという考えもあり、まずはエンジニアとして経験を積みたいと希望を出しました。 また、NTT Comのお客さまは主に法人で、情報システム部など、お客さまの担当者もエンジニアである場合が多いのをインターンシップで知っていました。そのため、将来的に営業をすることになってもエンジニアとしての共通言語を学んでおきたかったという思いもありました。 そして、入社後の初期配属はソリューションサービス部(以下、SS部)で、エンジニアとしての配属が決まりました。 文系・未経験だけれど… 私自身、ITに全く関心がなかったわけではありません。小学校低学年の頃から、帰宅したらPCにずっと張り付いてラジオを聴きながらWikipediaを徘徊するような生活をしていました。小学校高学年ぐらいになると、動画共有サイトが盛り上がってきて、友人と一緒に動画制作をしたり、Podcastを配信したりしていました。また、大学ではR言語を使ったデータ分析も経験しており、文系とはいえ免疫(?)はあった方だと思います。 NTT Com(現在、新卒はドコモグループとして採用し、入社は全員NTTドコモ)は新入社員研修で手厚いICT研修を受けられます。また、私の初期配属部署のSS部では、ネットワークからクラウド、アジャイル開発などの研修を受けました。研修期間中は同期内で自主的に研修テキストの輪読会を実施するなど、結構楽しかったことを覚えています。 SS部での研修後は、ICに10ヶ月間のOJTとして派遣されました。ここで、いいチーム・トレーナーとの出会いがありました。私の能力を上手く見定めて、「この資格はどう?」といった感じでスキルアップするヒントを貰っていました。そのサポートや、社内の資格取得制度も相まって、この3年半で10数個の資格を取りました。(実証実験で1週間出張した週末に応用情報技術者試験を受けたのは今でもいい思い出です。笑) 経験した業務 3年半の間で、私は2つの部署を経験しました。通常はあまりないケースですが、SS部(入社研修)→IC(OJT)→SS部→ICという異動でした。経験した業務の一部を列挙すると、以下のようなものがあります。 他社の協力を得て実施したローカル5Gの実証実験 全国各地でのモバイル計測 自社商材( Arcstar Universal One , Flexible InterConnect )を利用した閉域網からクラウドにアクセス可能な検証網の構築 商用案件の運用自動化開発 グループ内コミュニティイベント( dcc Engineer Day )の運営 社内向け技術広報ページの作成 「全国各地でのモバイル計測」時の写真。画面の反射を抑えるために日傘を使い、不審者味が増した。 見えてきた景色 この3年半、エンジニアとしてキャリアを進める中で悩んだこと、思ったこと、そして次へのアクションを書いていきます。 思い描いていたエンジニア像 皆さんが思い描くエンジニアはどういった人でしょうか? 私はコードをカリカリ書いて、土日は個人開発やハッカソンに勤しむ、そんな人物像を描いていました。 そんな人物像と裏腹に、3年経っても、そうなれていない自分について悩んだ日々もあります。アプリよりも、インフラ寄りの技術を扱う部署だったのもあるかもしれません。また、仕事とは別にやりたいことも多く、土日に個人開発やハッカソンへの参加をしたことはありません。4年目になった今でも、同じ会社のエンジニアの皆さんに限らず、他社のTechBlogで拝見するエンジニアの皆さんの背中は大きく見えます。 では自分はダメなのか? もちろん、エンジニアとしてのキャリアを積み、「スーパーエンジニア」を目指すのはアリだと思います。自分もスーパーエンジニアを目指すんだ!と思い込んだ時期もありましたが、一旦初心に返ってみました。すると、将来はサービス企画やマーケティング、営業といった仕事もしたいという希望を思い出しました。また、自分の性格的に、スペシャリスト型よりもジェネラリスト型のキャリアを望んでいる節もありました。 そこで、まずは元々興味があった情報発信や広報活動に、今の業務から関われないかと考えました。2年目の夏頃から、社内報で自らの部署のアピールをしたり、たまたま見かけたグループ内のエンジニアイベントの運営に手を挙げてみたりしました。環境にも恵まれ、結果として、チーム内外の色々な方々からこの動きを評価していただきました。 「エンジニア×◯◯」という発想 このように色々と動く中で、自分なりのエンジニア像を言語化できるようになってきました。それは、 エンジニアとしての技術理解を、情報発信や広報活動という自らの興味関心と組み合わせる というものでした。つまり、エンジニアとして技術の成果物を出すだけでなく、自分の得意分野や興味関心と組み合わせて、「エンジニア×何か」という複数の軸を持つという考え方です。 自分の中でこの方向性をイメージし始めてからは、何をすべきか?という迷いが減ってきました。今年は、今携わっている無線技術の社内向け広報ページをPJメンバーと共に作成する営みを通して、このエンジニア像に一歩近づくことができました。 別のことにトライしてみる エンジニアとして進んできた約3年半。 「新しいことを学んで身につけていくゲーム感覚」や、「未経験のことをする中で自分の強みを認識して生きる術を見つけること」、「目標を持って進むことの大切さ」など、ここでは書ききれないほどの学びを得ながら、エキサイティングな日々を送ってきました。 そんな中、「日本からゲームチェンジをしたい!」という入社当時の初心を思い返し、技術開発から、実際のお客さまにより近い立場での仕事に挑戦したいと思うようになりました。年明けの1月からはドコモグループ内で異動し、エンジニアとしての技術知識を活かしながら、ドコモのベニュービジネス 1 に携わる仕事をする予定です。 まとめ 写真やイラストがほとんどない記事でしたが、最後までお読みいただきありがとうございました!異動先でも何か発信するかもしれないので、見つけたら「あ、あの時の」と思い出していただけると幸いです。 スタジアムやアリーナ全体の運営ビジネスのこと。詳しくはリンク先を参照。 https://information.nttdocomo-fresh.jp/career/smart_life/service_list/venue/ ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 25日目の記事です。 はじめに プロダクトオーナーへのジョブチェンジの経緯 準備期間 稼働の移行 プロダクト全体像の把握 書籍による学習 引き継ぎとステークホルダーの理解 研究チームとの連携 本番期間 初期のフィードバック Howの介入を避けるための工夫 スプリントゴールの設定 開発者のモチベーション促進 開発者との協力体制 おわりに はじめに イノベーションセンターの杉本(GitHub: kaisugi )です。 私は現在、ノーコードAIモデル開発ツール Node-AI のプロダクト開発に携わっています。Node-AI は「AI を使って自社の課題を自分たちで解決できる会社を1社でも増やす」ことをビジョンに、スクラムによる内製開発を進めているプロダクトです。Node-AI の開発体制や技術スタックについては、Engineers' Blog の過去の記事でもいくつか紹介していますので、ぜひ以下の記事も合わせてご覧ください! engineers.ntt.com engineers.ntt.com engineers.ntt.com 今年度からの新たな取り組みとして、岩手大学と共同で、Node-AI を活用した「データサイエンス実践基礎」講義を開始しました。私も講師として、「第4回: 特徴量エンジニアリング」と「第8回: 生成AI」 1 の講義を担当しています。 www.ntt.com さて、私は新卒入社以来 Node-AI に開発者として携わってきましたが、最近、開発者からプロダクトオーナー(PO)へジョブチェンジしました。本記事では、元々開発者だったメンバーがプロダクトオーナーを担当してからの振り返りを紹介したいと思います。 プロダクトオーナーへのジョブチェンジの経緯 まず、なぜ開発者からプロダクトオーナーへのジョブチェンジを決めたのかお話しします。 私は開発者として Node-AI に携わる中で、エンジニアリングそのものよりも、AI を活用したビジネスをどうすれば上手く成功させられるかという部分に関心が移っていきました。特に昨今の生成 AI ブームで AI のビジネスへの適用範囲がさらに広がり、また同時に、エンジニアリングそのものが GitHub Copilot などの AI ツールに侵食され始めています。 エンジニアリングにおいて取り立てて強いスキルがない自分のキャリアとしては、AIのビジネス適用について頭を使う時間を増やした方が有益なのではないかと考え始めていました。そのような中で、先月にチーム体制の変化があったため、それに合わせて手上げでプロダクトオーナーを担当することにしました。 とはいえ、私はこれまでプロダクトオーナーを経験したことがなく、始めるまでは不安も当然ありました。特に、以前のプロダクトオーナーは強いオーナーシップでチームを牽引していたため、自分がうまく引き継げるのだろうかという点が心配でした。そのような不安を和らげるために、十分な準備期間を設けることにしました。 準備期間 稼働の移行 プロダクトオーナーは開発者とは全く異なるスキルが必要な仕事です。例えば、プロダクトオーナーはユーザーのフィードバックに毎日耳を傾けながら、プロダクトの成長戦略や開発の優先順位を考え、意思決定を行う必要があります。そのため、いきなり仕事を開発者からプロダクトオーナーに100%切り替えるのは難しいでしょう。 私の場合、プロダクトオーナーになる直前の2スプリント程度は、開発者としての稼働を段々と落としていくことにしました。 プロダクト全体像の把握 開発をしない代わりに浮いた時間で、プロダクトの全体像の把握を改めて自分で行いました。特に念入りに行ったこととして、Node-AI におけるプロダクトの4階層(Core, What, Why, How)を改めて自分の中で整理し、その中で直近行われている開発がどのような価値をもたらすものかを再確認しました。 書籍による学習 以前のプロダクトオーナーに勧められた本の中でも、『 ユーザーストーリーマッピング 』は特に腹落ちする内容が多い本でした。この本の中に「 岩を砕く 」という比喩がありますが、プロダクトオーナーの仕事のあらゆる部分でこの感覚が重要になります。ビジネスサイドにとってのプロダクト開発のストーリー(大きな岩)と、開発者にとってのプロダクト開発のストーリー(小石)は粒度の全く異なるものであり、これらを素早く行き来しながらコミュニケーションを取れることが必要です。 開発者として仕事をしていると、どうしても後者のストーリーにばかり目を向けがちですから、根本的な思考の切り替えが求められました。 引き継ぎとステークホルダーの理解 書籍による座学だけでは難しい部分もあるため、以前のプロダクトオーナーの発言や行動を観察しながら、そのプロダクトオーナーの脳内を自分でもトレースできるように努めました 2 。 プロダクトオーナーになる上では、プロダクトそのものだけでなく、プロダクトの外のさまざまな ステークホルダー についても理解する必要があります。個人的には、この部分が複雑で、一番難しいと感じました。その点において、以前のプロダクトオーナーからは、十分に時間をかけてステークホルダーについて解説していただいたので、大変感謝しています。 研究チームとの連携 Node-AI チーム特有の話として、Node-AI に搭載される技術の要素技術である時系列分析の研究チーム(先端AI数理プロジェクト)が隣で活動している点があります。研究チームは我々のようなスクラムで活動しているわけではないため、研究成果を Node-AI に搭載していくためには一種の調整が必要になります 3 。チーム間の連携の仕方は正直に言って現状ベストなものではなく、試行錯誤の最中であり、プロダクトオーナーから課題を引き継ぐ形になりました。 本番期間 初期のフィードバック プロダクトオーナーを担当し始めてから最初の2スプリント程度は、レトロスペクティブとは別に、スクラムマスターからフィードバックを受ける時間を設けていただきました。自分1人で振り返りを行うと、反省ばかり先に頭に浮かんでしまうものですが、スクラムマスターは客観的にポジティブフィードバックも行なってくれるのでありがたい存在です。個人的には、スクラムマスターからのフィードバックは初期にしっかりもらうと良いと思います。 Howの介入を避けるための工夫 開発者からプロダクトオーナーになる上で一番危惧していたのは、開発者の頃の癖で、開発の細かいHowにまで自分が突っ込みたくなってしまうことでした。機能の詳細や仕様の議論に時間を割くのは、 プロダクトオーナーのアンチパターン の1つです。そのため、プロダクトオーナーになってからは、しきりに「Howは皆さんにお任せする」ということを口癖のように繰り返していました。結果、今のところ、この部分は問題なく進められているように感じます。 スプリントゴールの設定 プロダクトオーナーとして日々難しいと感じているのは、毎週のスプリントゴールに何を立てるか、どんな価値を見せていくかというところです。機能は作れば作るだけいいというものではありません。むしろ、 機能は作った瞬間から負債になる ので、本当に価値をもたらさない限り機能は作らない方が良いです。 一方で、開発者からすると、新機能を作るPBIではなく細かな修正やアップデートのPBIばかりこなしていると、プロダクトが前に進んでいるのか分かりづらく、モチベーションを上げづらいという問題もあります。そのためプロダクトオーナーは、今はこういうことに注力している、だからこのスプリントゴールを立てているのだ、という思想を語り続ける必要があります。 開発者のモチベーション促進 Node-AIの開発チームでは現在、運用系のタスクにこれまで以上にしっかりと取り組んでいくことにモチベーションが向かっています。これは、Node-AI のユーザー数が以前よりも増えており、以前のプロダクトフェーズでは後回しにしていたタスク(例: メトリクス・ログ・トレースの集計や SLO の追跡など)の必要性が高まったためです。 運用系タスクはステークホルダーに価値を直接見せられるものではないので、プロダクトオーナーとして配分には熟慮が必要ですが、なるべく毎スプリントコツコツと積んで開発者の中でも習慣化できるようにしたいと考えています。 開発者との協力体制 私は、PBI の優先順位やその積み方について疑問がある場合、すぐに 開発者に相談する ように心掛けています。これは、引き継ぎの際に、以前のプロダクトオーナーが「開発者の中でも開発の進め方そのものをもっと議論できるようにしたい」と言っていたからです。以前のプロダクトオーナーにはカリスマ性があり、「何でも任せて大丈夫」という信頼感が生まれていた反面、開発者の間で議論があまり行われていなかったことを課題感として持っていたようです。 プロダクトオーナーとして、最終的な責任を持つことは重要ですが、全てを一人で決めるのではなく、開発者が自主的に議論できる雰囲気を作ることも大切だと考えています。もちろん、全てを開発者任せにしてしまうと負担が大きくなり、リファインメントが難しくなることもあるため、適切な線引きは必要です。 おわりに この記事では、開発者からプロダクトオーナーになった私の体験談を赤裸々に紹介させていただきました。当初は不安もありましたが、今は少しずつ走り出せているという感触です。 本記事が、新米プロダクトオーナーとして苦労されている誰かの力になれば幸いです。 それでは皆さん、メリークリスマス! 生成 AI といえば、余談ですが、私は日本語に特化した LLM に関する情報を集めるのが趣味で、「 日本語 LLM まとめ 」という GitHub リポジトリを日々メンテナンスしています。今年は GitHub のスター数が 1,000 を超えたので嬉しいです! ↩ このあたりの引き継ぎの仕方は、チームの方針によって最適な方向があるかと思います。私自身は、まずは以前のプロダクトオーナーの方針を踏襲して進めていくことをスクラムマスターに宣言していました。 ↩ Node-AI というプロダクト起点でストーリーを考えた場合、Node-AI というプラットフォームに、研究成果であるさまざまな AI モデルが搭載されていきます。一方で、Node-AI には適さないが有用な研究成果の場合、研究成果を起点に新たなプロトタイプを作成することもあります。チーム全体でのこれらの活動の進め方の配分をどのように行っていくかは難しい問題であり、マネージャー層の方々が苦労される部分ではあります。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 24日目の記事です。 システムを運用していると日々のアラートメールへの対応の手間を減らしたいと感じることはありませんか? 例えばセキュリティアラートに日々対応している運用者の方は、アラートの中に含まれる誤検知・過検知への対応に負担を感じている方も多くいらっしゃると思います。 この記事では、そのような誤検知・過検知対応の負担を削減するためにサーバーの脆弱性通知メールの一次切り分けを、マイクロソフト社が提供しているローコードツールであるPower Automateを使って自動化した話を紹介します。 はじめに 脆弱性通知メールの一次切り分けを自動化したいと思った背景 作成したPower Automateのフロー フロー作成において大変だった点 NVDのAPIが採用しているCPE(ソフトウェア名やバージョンの識別子)を利用した脆弱性の突合が難しい フローのアクションの実行回数に制限がある フローを作成した結果 まとめ はじめに こんにちは。ソリューションサービス部の牧です。 普段はセキュリティソリューションの開発やプリセールスの業務を行っています。 この記事では、私が所属しているチームで管理しているLinuxサーバーの脆弱性通知メールの一次切り分けを、マイクロソフト社が提供しているローコードツールであるPower Automateを使って自動化した話を紹介します。 脆弱性通知メールの一次切り分けを自動化したいと思った背景 私が所属しているチームでは、検証用にLinuxサーバーを運用しています。社内の規定に従って、ソフトウェアなどで発見された脆弱性の対策状況を管理するシステムにサーバーのOSなどの情報を登録しており、ほぼ毎日2通~5通ほどの新規の脆弱性の通知メールを受信しています。 新規の脆弱性が発見された場合、脆弱性の深刻度に応じて対策完了までの期限が定められているため、その期限までに対策を完了させる必要があります。 そのため定期的に脆弱性通知メールの内容を確認し、サーバーにログインして脆弱性の発見されたソフトウェアがインストールされているかどうかを確認して、必要であればパッチ適用を実施しています。 脆弱性通知メールの中にはサーバーにインストールしていないソフトウェアのものも多く含まれており、毎回サーバーにログインして切り分けをするのは大変なので、Power Automateを活用することでこの確認作業の手間を減らしたいと考えました。 作成したPower Automateのフロー 作成したPower Automateのフローの処理の流れは以下の通りです。 脆弱性通知メールを読み取る メール本文からCVE-IDを抜き出す CVE-IDが含まれない場合、「CVD-IDなし」フォルダにメールを振り分ける Canonical社が公開しているUbuntu Security APIからCVE-IDを使って脆弱性のあるパッケージ名とバージョンを取得する 予め保存しておいたインストール済みのパッケージ一覧に脆弱性のあるパッケージがあるか突合する パッケージ名とバージョンがどちらも一致した場合、「パッチ適用が必要」フォルダにメールを振り分ける パッケージ名のみ一致した場合、 「パッチ適用済み」フォルダにメールを振り分ける パッケージ名とバージョンがどちらも一致しない場合、「該当なし」フォルダにメールを振り分ける 実際に作成したPower Automateのフローは以下の通りです。 フロー作成において大変だった点 フローの作成において大変だった点は以下の2点です。 NVDのAPIが採用しているCPE(ソフトウェア名やバージョンの識別子)を利用した脆弱性の突合が難しい 当初は脆弱性のあるパッケージの突合にCanonical社が公開しているUbuntu Security APIではなく、NVD(National Vulnerability Database)という最も広く利用されている脆弱性情報データベースのAPIを利用しようとしていました。 NVDでは、脆弱性情報データベース内の脆弱性識別子CVEと結びつけられたCPEという識別子がソフトウェアと脆弱性を突合させるために利用されています。 例えば、Microsoft社のInternet Explorer 8.0.6001 BetaをCPEで表現すると、 cpe:/a:microsoft:internet_explorer:8.0.6001:beta となります。 しかし、一般的に全てのソフトウェアに対して普遍的な識別子は存在せず、ソフトウェアを一意に特定することは難しいと言われています。 OSSのTrivyも、CPEを使わず各ベンダーが出している脆弱性情報を使いマッチングを行っています( 参考 )。 そのため、今回は複雑なスクリプト実行ができないこともあり、CPEではなくaptコマンドで取得できるソフトウェアの名前とバージョンと同じ形式で脆弱性のあるソフトウェアの名前とバージョンを取得できるCanonical社が公開しているUbuntu Security APIを利用することにしました。 フローのアクションの実行回数に制限がある クラウド版の場合のフローの開発は、主に「①フロー作成画面で一連のアクションを設定する」→「②フローを実行してテストする」→「③実行結果を確認してエラーを修正する」→①に戻るという流れで進めると思いますが、開発が進んでアクションの数が増えるに従ってフローのアクションの実行回数をたくさん消費してしまっていたようで、実行回数の制限の80%に到達したところで警告メールが送られてきました( 参考 )。 制限を超えるとアクションの実行が制限されたり、遅くなったりする可能性があるそうなので、制限を超えないように気を付ける必要がありました。 フローを作成した結果 フローを作成した結果、下記の画像のように脆弱性の突合結果に応じて自動でメールのフォルダが振り分けられるようになり、毎回サーバーにログインしたり脆弱性のあるソフトウェアがインストールされているかどうかを確認する必要がなくなりました。 パッチを適用する際にはサーバーにログインする必要はあるのですが、特に誤検知・過検知への対応の手間を減らすことができました。 まとめ 今回はサーバーの脆弱性通知メールの一次切り分けをマイクロソフト社が提供しているローコードツールであるPower Automateを使って自動化した話を紹介しました。 最後までご覧頂き、ありがとうございました!明日の記事もお楽しみに。
アバター
この記事は、  NTT Communications Advent Calendar 2024  23 日目の記事です。 本記事では、防衛や重要インフラなどで利用されることがある、データダイオード(片方向の通信のみを許可する装置)の動作と原理を理解するために、シンプルなデータダイオードを作成します。 具体的には、メディアコンバータ等の手軽に入手できる市販のネットワーク機器を使ってデータダイオードを作成し、実際に動作することを確認します。 データダイオードとは? 想定される利用シーン 可用性を重視するシステムを保護したい場合 機密性を重視するシステムを保護したい場合 データダイオードを作ってみる メディアコンバータ3台を利用したデータダイオード つくってみる 動作確認 光ファイバースプリッターを利用したデータダイオード つくってみる 動作確認 おわりに こんにちは! 最近、データダイオードにはまっている上田です。 普段はOT(制御システム)セキュリティ製品の検証や、NTT Comが内製開発している OsecT の開発に関わっています。 この記事は、市販されているネットワーク機器を利用して、データダイオード(ハードウェア部分)を作れないか試してみたものになります。 あくまでも検証用として作成したものなので、実務レベルのシステムではありません(業務環境で利用したことも、利用する予定も今のところありません)。本記事の情報を参考にされる場合は、自己責任でお願いします。 なお、タイトルに「その1」とつけましたが続編は未定です。筆者のやる気が出たら出せる範囲で出します。 この記事でやること・やらないことは下記のとおりです。 やること ハードウェアの仕組みを利用したデータダイオードの作成 動作確認 やらないこと ソフトウェアの作成 パフォーマンステスト 電子工作 データダイオードとは? データダイオード(Data Diode)は片方向の通信のみ許可(強制)する装置です。 一般に、ハードウェアを利用して片方向通信を強制するものをさすことが多い印象です。 このため、脆弱性や設定ミス、管理者権限の窃取などにより突破される可能性があるファイヤーウォール(FW)などと比べて、(設置方法を間違えなければ)強固なセキュリティを確保できるようです。 ちなみに、データダイオードは、Unidirectional Gatewayと呼ぶこともあるようです。 ただ、場合によってはData DiodeとUnidirectional Gatewayそれぞれ使い分けることもあるようです。 データダイオードとUnidirectional Gatewayの定義は組織によって異なることもあるようですが、アメリカ国立標準技術研究所のOTセキュリティガイドである NIST SP 800-82r3 によると、下記のように定義されていました。 data diode A network appliance or device that allows data to travel only in one direction. Also referred to as a unidirectional gateway, deterministic one-way boundary device, or unidirectional network. 1 unidirectional gateway Unidirectional gateways are a combination of hardware and software. The hardware permits data to flow from one network to another but is physically unable to send any information at all back to the source network. The software replicates databases and emulates protocol servers and devices. 2 上記の定義によると、データダイオードは片方向の通信のみを許可する機器全般のことをさし、Unidirectional Gatewayは、ハードウェアによって片方向通信を強制しつつソフトウェアによって通信相手のデバイスやサーバをエミュレートする機能などを持つ機器のことをさすようです。 実際にWeb上を検索してみると、Unidirectional Gatewayと銘打っている製品は、TCPのようにコネクション型のプロトコルをはじめ、さまざまなプロトコルに対応している製品が多い印象です。 なお、今回作成するのはUnidirectional Gatewayではなく(原始的な?)データダイオードになります。 想定される利用シーン データダイオードが利用される分野としては、防衛や重要インフラなどの分野で使われることが多いようです。 最近の例では、アメリカのマイアミ港のクレーン管理システムを保護するために、データダイオードを導入するようです 3 。 データダイオードの設置方法は、可用性と機密性という観点に着目すると次の2つの場合に分けることができそうです。 可用性を重視するシステムを保護したい場合 1つめの例は、制御ネットワークなど、攻撃されると大きな事故につながる可能性があるネットワークを外部の攻撃から守りたい場合です。 そもそも、他のネットワークと接続しなければ良いのかもしれません。しかし、どうしても重要度の高いネットワークの外にネットワーク経由で情報を送りたいといった需要が発生することもあります。 そのような場合に、FWよりも強固なセキュリティを確保できるデータダイオードを採用することがあるようです。 この例では、可用性を重視するネットワーク外からの攻撃をブロックしつつ、外部へデータを送信することが可能になります。 機密性を重視するシステムを保護したい場合 2つめの例は、重要なデータを保管したり分析するためのネットワークからの情報流出を阻止したい場合です。 こちらも、他のネットワークと接続しなければ良いのかもしれません。しかし、必要なデータは他のネットワークから適宜取得できるようにしたいといった需要が発生することもあります。 このような場合にもデータダイオードを採用することがあるようです。 この例では、機密性を重視するネットワーク外への情報流出をブロックしつつ、外部からのデータ受信が可能になります。 ただ、いずれの場合もただ単にデータダイオードを導入するだけで十分なセキュリティを担保できるわけではないと考えます。たとえば、データダイオードによりネットワークのデータの流れを制限したとしても、ソフトウェアアップデートのために記憶媒体などを利用してデータダイオードによって許可したデータの流れとは逆方向に、データを移動させる必要がある場面などはどうしても出てくると思います。また、保護対象のネットワークが知らないうちに他のネットワークとつながっている可能性や、既存のデータダイオードと新たに導入するデータダイオードを組み合わせると双方向通信が可能になってしまうといったことが無いかなどを確認するといったことも必要になるかと思います。そのため、データダイオードの導入が本当に有効なのかを見極めつつ、記憶媒体の検疫やネットワークの可視化・検知など複数のセキュリティ対策を組み合わせることが重要になると考えます。 データダイオードを作ってみる 前章までは、一般的なデータダイオードに関して説明しました。 この章からは、データダイオードを実際に作ってみます。ただ、あくまでも検証レベルで、実用する際に必要な機能の実装や検証は行いません。 今回は、先行事例を参考にしながら自宅のネットワーク機器を利用して作成を試みます。 メディアコンバータ3台を利用したデータダイオード 1つめの自作データダイオードの先行事例としては、Arnaud Soullié氏らによって2016年のBSides Las Vegas等で発表された DYODE (Do Your Own Diode) があります 4 。 DYODEはDYODE v1とDYODE v2の2つのバージョンがあるようです。v2はフォトカプラを利用しており、電子工作の必要があります。対して、v1はメディアコンバータを利用しており、手軽に作ることができそうです。 そこで、まずはDYODE v1を参考にデータダイオードを作ってみます。 つくってみる 用意したもの下記のとおりです。 メディアコンバータ(DN5810SG2E)3台 LC-LCケーブル:2本 Raspberry Pi 4 Model B 8GB:2台 その他周辺機器:電源、LANケーブル等 配線は下記のようになりました。 DYODE v1と同じように、送信側メディアコンバータINの発光(TX)ポートと、受信側メディアコンバータOUTの受光(RX)ポートを光ケーブルで接続します。 この状態では、受信側のメディアコンバータOUTはリンクアップしますが、送信側のメディアコンバータINはリンクダウンしたままになります。 そこで、送信側のメディアコンバータINの受光(RX)ポートと3台目のメディアコンバータNCの発光(TX)ポートを接続します。こうすることで、3台目のメディアコンバータNCから送信用メディアコンバータINに信号が送られ、送信側メディアコンバータINは対向機器が接続されたと判断し、リンクアップします。 下記の画像は、実際に自宅の壁に設置した際の様子です。 余談ですが、電源やスイッチ、Raspberry Pi、メディアコンバータをDINレールに固定するためのパーツや、DINレールをピクチャーレールに固定するためのパーツは、FreeCADと3Dプリンタを使って自作しました。FreeCADは最近大幅にアップデートされ、以前よりも使いやすくなっているようなので近々アップデートして使ってみたいと思っています。 動作確認 下記の送信デバイスと受信デバイスの双方から、お互いにpingコマンドを打ってみます。 下記画像は、実際にpingコマンドを実行した際のCLIの様子です。 左の上下2つのCLIが送信デバイス(pi4b-8gb-exp01)、右の上下2つのCLIが受信デバイス(pi4b-8gb-exp02)のものになります。 上部にpingコマンドの実行結果、下部にtcpdumpコマンドの実行結果が出力されています。 下部のtcpdumpコマンドの実行結果を確認すると、pingコマンドによる受信デバイスへのARP要求パケットは正常に届いていることが分かります。 対して、受信デバイスからのARP応答パケットやICMPエコー要求パケット、ARP要求パケットは送信デバイスに届いていないことを確認できます。 これにより、データダイオードとして正常に機能していることを確認できました。 パケットの流れを整理すると下記のようになります。 ちなみに、tcpdumpコマンドの出力結果に表示されているARPパケットのサイズ( length )が送信時は28なのに対して、受信時は46と増えていますが、こちらはEthernetの仕様(Ethernetフレームのデータ部分は最小46byte)を満たすためにパディングが追加された結果と思われます。 光ファイバースプリッターを利用したデータダイオード DYODE v1は手軽に構築できるのですが、メディアコンバータを3台用意しなければならないというデメリットがあります。 そこで、このデメリットを解消する方法が無いかと、データダイオードについて調べたところ、 OSDD というオープンソースプロジェクトを見つけました。 OSDDはデータダイオードの作り方が複数用意されているようです。複数ある作り方の1つに、光ファイバースプリッターを利用する方法がありました 5 。 こちらの方式は、光ファイバースプリッターさえ用意できれば手軽にデータダイオードをつくることができそうです。そこで、今回はこちらの方法を試してみます。 つくってみる 用意したもの下記のとおりです。 メディアコンバータ(DN5810SG2E)2台 1対2光ファイバースプリッタ―:1本 Raspberry Pi 4 Model B 8GB:2台 その他周辺機器:電源、LANケーブル等 配線は下記のようになりました。 参考文献と同じように、送信側メディアコンバータINの発光(TX)ポートと、光ファイバースプリッターを接続します。そして、2つに分岐したケーブルを各メディアコンバータの受光(RX)ポートに接続します。 こうすることで、送信側メディアコンバータINと受信側のメディアコンバータOUTの両方がリンクアップします。 下記の画像は、実際に自宅の壁に設置した際の様子です。 動作確認 下記の送信デバイスと受信デバイスの双方から、お互いにpingコマンドを打ってみます。 下記画像は、実際にpingコマンドを実行した際のCLIの様子です。 左の上下2つのCLIが送信デバイス(pi4b-8gb-exp01)、右の上下2つのCLIが受信デバイス(pi4b-8gb-exp02)のものになります。 上部にpingコマンドの実行結果、下部にtcpdumpコマンドの実行結果が出力されています。 下部のtcpdumpコマンドの実行結果を確認すると、pingコマンドによる受信デバイスへのARP要求パケットは正常に届いていることが分かります。 対して、受信デバイスからのARP応答パケットやICMPエコー要求パケット、ARP要求パケットは送信デバイスに届いていないことを確認できます。 これにより、データダイオードとして正常に機能していることを確認できました。 メディアコンバータ3台を利用した DYODE v1 との差分は、送信デバイス側で2倍のARP要求パケットが観測されている点です。これは下記動画のように、スプリッタ―を経由して送信したパケットが再度、送信デバイス側に送られてくるためです。 このため、同一のブロードキャストドメインに「メディアコンバータIN」が2台以上存在してしまった場合など、 構成によってはブロードキャストストームが発生してしまいます(止まって困るようなネットワーク機器には接続しないことをおすすめします) 。 なおこちらもEthernetの仕様により、tcpdumpコマンドの出力結果に表示されているARPパケットのサイズ( length )が送信時は28なのに対して、受信時は46とパディング分が増えています。 おわりに 最後まで読んでくださりありがとうございました。 今回は、メディアコンバータと光ファイバースプリッタ―を利用したデータダイオード(ハードウェア部分)を作ってみました。 動作がシンプルなだけに使いどころが難しいデータダイオードですが、自宅ネットワークに導入するなどして遊べたらなと考えています。 もし機会があれば、ソフトウェアの部分まで踏み込んだ続編を(可能な範囲で)書きたいと思います。 それでは、明日のアドベントカレンダーもお楽しみに。 NIST SP 800-82r3 161頁 より。 ↩ NIST SP 800-82r3 168頁 より。 ↩ Waterfall社のニュースリリース「 Waterfall and GoCloud Forge Powerful New Partnership to Secure North-America’s Largest Maritime Port 」より。 ↩ 発表の様子(YouTube): DYODE: Do Your Own DiodE for Industrial Control Systems - AryKokos, Arnaud Soullie ↩ OSDD: Example data-diode hardware setups ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 22日目の記事です。 こんにちは、イノベーションセンターの鈴ヶ嶺です。普段はAI/MLシステムに関する業務に従事しています。 本記事では、LLMのRetrieval Augmented Generation(RAG)などで用いられる近似最近傍探索(ANN)手法の1つとして最近注目されているDiskANNについて紹介します。 DiskANNはbillionクラスの大規模なデータセットに対してスケーラブルに機能し、SSDなどのDiskを利用可能なことからコスト効率の良い点が特徴です。 本記事ではまず、グラフベースの近似最近傍探索と従来手法の課題点について説明します。次にDiskANNについて、中核となるアルゴリズムであるVamana AlgorithmやDiskへのデータ保存の詳細、検索方法、利用方法について説明します。さらに、DiskANNベースの手法を採用しているクラウド・ベクトルデータベースベンダーの対応状況を紹介します。 グラフベースの近似最近傍探索について DiskANN Vamana Algorithm Diskへのインデックス 派生手法 FreshDiskANN Filtered-DiskANN 利用方法(diskannpy) クラウド・ベクトルデータベースベンダーの対応状況 Azure DiskANN Vector Index in Azure Database for PostgreSQL DiskANN for Azure Cosmos DB Timescale pgvectorscale Zilliz Milvus Weaviate まとめ グラフベースの近似最近傍探索について 近似最近傍探索は与えられた検索対象のクエリから、類似される(距離が近い)データを効率的に見つけることが目的です。 その中で、グラフベース手法はデータセットをノードとエッジにより構成されたグラフで表現して探索するものになります。 例えばグラフベース手法で有名なHierarchical Navigable Small World(HNSW) 1 は、次のように階層的なグラフ構造となります。 上層では疎なグラフを探索して、徐々に階層を重ねることでより密なグラフの探索をします。 引用: https://arxiv.org/abs/1603.09320 このような階層性や探索におけるランダムアクセス性の要件からHNSWのグラフはin-memoryに保存することが望まれます。 一方で大規模なデータセットをメモリに展開するのはコストが高く、利用が難しい状況にあります。 また、その他グラフベース手法の詳細については説明を割愛しますが、以下の松井先生の資料などがわかりやすいため補足としてご覧いただければと思います。 speakerdeck.com DiskANN HNSWなどのグラフベース手法の課題を背景に、Microsoft Researchの研究チームはbillionクラスの大規模なデータに対してスケーラブルに機能し、さらに低コストなSSDなどのDiskを用いることが可能な手法であるDiskANNを考案しました。 この手法は2019年のNeural Information Processing Systems(NeurIPS)で採択されています。 2 実装は、オープンソースとして公開されています。 https://github.com/microsoft/DiskANN コアはC++で実装されており、Pythonの diskannpy からも利用可能です。 一部Rustによる実装も進められていますが、コミッターの回答からは明確なロードマップがあるわけではないようです。 We do not have a strong roadmap yet. https://github.com/microsoft/DiskANN/issues/409 Vamana Algorithm このセクションではDiskANNのグラフ作成アルゴリズムであるVamana Algorithmについて説明します。 Vamana Algorithmはグラフの走査を高速化するためのもので、そこには2つのアルゴリズムが寄与しています。 Vamanaのグラフはフラットな単一のレイヤーからなるグラフで構成されます。 最初にランダム接続された初期グラフを改善することによってクエリを探索可能なグラフとして構成します。 引用: https://proceedings.neurips.cc/paper_files/paper/2019/file/09853c7fb1d3f8ee67a61b6bf4a7f8e6-Paper.pdf Algorithm 1のGreedySearchでは、入力されたクエリにより距離が近いノードを探索します。 始点ノードであるsから、クエリx_qにより近い隣接ノードを探索して候補に加えて、さらにその候補から隣接ノードを探索するというような形で、よりクエリx_qとの距離を縮める方向で貪欲的に探索を進めます。 探索中に候補数がL以上になった場合、上位Lのノードだけ残して探索を進めます。 この処理は、後に説明するAlgorithm 3のグラフ構築時にも、クエリの検索時にも利用されます。 引用: https://proceedings.neurips.cc/paper_files/paper/2019/file/09853c7fb1d3f8ee67a61b6bf4a7f8e6-Paper.pdf Algorithm 2のRobustPruneではノードのエッジの刈り込みを行いグラフ探索の効率を向上させます。 まず、特定のノードpを起点として、候補ノードの中から最も距離が近いノードp*を選択し、pとp*の間にエッジを新規に追加します。 次に候補から任意のノードp'において、追加されたノードp*からp'の距離をパラメータαの乗数で制御して、特定のノードpからp'の距離よりも小さい場合、p'を削除します。 この処理で、ある程度疎なグラフとなり、長距離のエッジも形成されます。 最終的にノードあたりのエッジ数はR個になるように調整されます。 上図に、RobustPruneにおけるパラメータαによる違いを例示します。 α=1の場合、ノードp'は削除されます。一方、α=2の場合、削除されず長距離のエッジ追加に貢献します。 ちなみに、HNSWやNSG 3 では、この調整可能なパラメータαを持っておらず、暗黙的にα=1を利用しているため長距離のエッジが作成されにくい傾向にあります。 引用: https://proceedings.neurips.cc/paper_files/paper/2019/file/09853c7fb1d3f8ee67a61b6bf4a7f8e6-Paper.pdf グラフ構築のインデックス処理はAlgorithm 1, 2を組みわせて行われます。 まず、ランダムに設定されたノードをデータセットのmedoid(全てのノードからの距離の合計が最小)からGreedySearchを行い探索で訪れたノードを記録します。 それらのノードは長距離のエッジ構築を促進する目的で、次のRobustPruneの候補ノードとして活用され、エッジの刈り込みがされます。 これらの処理を行いエッジが刈り込まれて、探索の効率が向上します。 次の画像は、実際にエッジが刈り込まれる様子を図示されたものになります。 1回目にα=1として処理され、次の2回目に長距離エッジを導入するためにユーザ定義のα>1で処理されます。 引用: https://proceedings.neurips.cc/paper_files/paper/2019/file/09853c7fb1d3f8ee67a61b6bf4a7f8e6-Paper.pdf Diskへのインデックス billionスケール規模のデータセットにおいて、例えばfloat32の100次元データとして考えるとインデックス構築時には4Byte x 100 x 1000000000 = 約373GBほど必要となります。 先述の通り、これをメモリに展開することはコストが高く、小規模な計算環境では現実的ではありません。 DiskANNではクラスタリングによる分割によってこの問題の解決を図ります。 k-meansを利用してk個(例 k=40)のクラスタに分割して各データセット点はl個(例 l=2)のクラスタに属するように設定します。 次に、各クラスタに割り当てられたデータセットでVamamaインデックスを構築します(データ数はNl/kとなり、例 4Byte x 100 x 1000000000 x 2 / 40 = 約 18.6GB)。 最終的に各クラスタで構築したグラフのエッジを単純に結合させて1つのグラフにマージします。 最終的に作成したグラフを全てメモリに載せることは難しいので、DiskANNではProduct Quantization 4 を利用した圧縮ベクトルをメモリに格納して、フル精度のグラフはSSDなどのDiskに保存します。Diskへの保存のレイアウトは各ノードiについてフル精度のベクトルとR個の近傍ノードのIDを記録します。その際にノードの次数がRより小さい場合は0埋めしてオフセットの計算を容易にします。 クエリ検索時には圧縮ベクトルを用いてまず大まかな計算をして、実際の距離を計算してランク付けする処理はDiskにあるフル精度のものを利用します。検索の様子は次のMicrosoftが公開している動画が非常にわかりやすく表現されているのでおすすめです。 www.youtube.com 派生手法 DiskANNを提案したMicrosoft Researchの研究チームはDiskANNを改良した派生を活発に研究しています。 FreshDiskANN DiskANNはランダムな初期グラフから刈り込みをすることでグラフを生成するため、新しいデータを追加して再インデックス化するコストがかかっていました。 その課題に対してFreshDiskANN 5 では、リアルタイムにデータを更新してインデックスに反映可能な手法を提案しています。 Filtered-DiskANN Filtered-DiskANN 6 は日付、価格帯、言語などでラベルによってフィルタリングされたインデックス内の探索を効率化した手法を提案しています。 利用方法(diskannpy) DiskANNのPythonでの実装、 diskannpy の利用方法は次のようになります。 sample_diskann.py import numpy as np import diskannpy as dap from pathlib import Path rng = np.random.default_rng( 1234 ) rows = 512 dim = 100 data = rng.random((rows, dim), dtype=np.float32) index_directory = "index" Path(index_directory).mkdir(exist_ok= True ) # Build Disk Index dap.build_disk_index( data=data, distance_metric= "l2" , index_directory=index_directory, graph_degree= 16 , complexity= 32 , vector_dtype=np.float32, search_memory_maximum= 0.00003 , build_memory_maximum= 1 , num_threads= 0 , pq_disk_bytes= 0 ) index = dap.StaticDiskIndex( distance_metric= "l2" , vector_dtype=np.float32, index_directory=index_directory, num_threads= 16 , num_nodes_to_cache= 10 , ) # Search queries = rng.random(( 5 , dim), dtype=np.float32) identifiers, distances = index.batch_search(queries, k_neighbors= 5 , complexity= 5 , beam_width= 2 , num_threads= 1 ) 実行方法は次のようになり、indexディレクトリにメタデータやインデックスが保存されます。 python sample_diskann.py tree index/ # Diskに保存されたメタデータやインデックス # index/ # ├── ann_disk.index # ├── ann_metadata.bin # ├── ann_pq_compressed.bin # ├── ann_pq_pivots.bin # ├── ann_sample_data.bin # ├── ann_sample_ids.bin # └── ann_vectors.bin クラウド・ベクトルデータベースベンダーの対応状況 このセクションではDiskANNベースのアプローチをとり実装している各種クラウド事業者やいくつかのベクトルデータベースベンダーの対応状況を記載します。 Azure Microsoftで考案された手法であるため、クラウドにおいてはAzureでの利用が先行している印象です。今年2024年のMicrosoft Build 2024やMicrosoft Ignite 2024でAzure Database for PostgreSQL - Flexible ServerとAzure Cosmos DBの対応が発表されました。 DiskANN Vector Index in Azure Database for PostgreSQL 引用: https://techcommunity.microsoft.com/blog/adforpostgresql/introducing-diskann-vector-index-in-azure-database-for-postgresql/4261192 Azure Database for PostgreSQL(Flexible Server)において pg_diskann 拡張を有効化することで利用可能となりました。 次のような例でDiskANNを使用可能です。 -- pg_diskannのextension有効化 CREATE EXTENSION IF NOT EXISTS pg_diskann CASCADE; -- インデックスをdiskannを利用して作成 CREATE INDEX my_table_embedding_diskann_idx ON my_table USING diskann (embedding vector_cosine_ops); DiskANN for Azure Cosmos DB 引用: https://devblogs.microsoft.com/cosmosdb/diskann-for-azure-cosmos-db-now-in-open-public-preview/ Azure Cosmos DBでもDiskANNが利用可能です。Cosmos DBの水平スケーリング機能と相乗効果で多くのクエリをコスト効率を最適化しながら処理することが可能となります。 次のように EnableNoSQLVectorSearch を設定することで利用可能です。 az cosmosdb update \ --resource-group <resource-group-name> \ --name <account-name> \ --capabilities EnableNoSQLVectorSearch Timescale pgvectorscale Timescaleはpostgresql拡張として pgvectorscale をOSSとして公開しています。 内部ではStreamingDiskANNというDiskANNにインスパイアされたアルゴリズムを使用しています。 余談ですがpgvectorscaleはRustでPostgresql拡張を実装する PGRX framework を用いてRustで実装されています。 TimescaleはいくつかDiskANNに関する技術ブログを公開しているため参考におすすめです。 https://www.timescale.com/learn/hnsw-vs-diskann https://www.timescale.com/blog/understanding-diskann Zilliz Milvus Zillizのベクトルデータベース Milvus はDiskANNに対応しています。インデックスタイプを DISKANN と設定することで利用可能です。 https://milvus.io/docs/ja/disk_index.md ZillizはDiskANNに関する技術ブログを多数公開しています。 https://milvus.io/blog/2021-09-24-diskann.md https://zilliz.com/learn/DiskANN-and-the-Vamana-Algorithm https://zilliz.com/blog/diskann-a-disk-based-anns-solution-with-high-recall-and-high-qps-on-billion-scale-dataset Weaviate ベクトルデータベース Weaviate はVersion 1.18からDiskANNベースの処理を組み合わせています。 More specifically, in 1.18 we implemented the solution proposed in DiskANN whereby we combined our HNSW indexing with vectors that have been compressed using product quantization (PQ). 引用: https://weaviate.io/blog/weaviate-1-18-release WeaviateもDiskANNに関する技術ブログを公開しています。HNSWとのRecallやLatencyなども比較検証結果を公開しています。 https://weaviate.io/blog/ann-algorithms-vamana-vs-hnsw まとめ 本記事ではDisk-friendlyで低コストにスケール可能な近似最近傍探索手法 DiskANNのグラフ構築アルゴリズムVamanaやその利用方法を紹介しました。また、各クラウド・ベクトルデータベースベンダーの対応状況を紹介し低コストで社会実装を視野に入れた取り組みが活発化している印象を感じています。大規模な検索システムを検討している方にこちらの内容を参考にしていただければ幸いです。 明日のアドベントカレンダーもお楽しみに。 Malkov, Yu A., and Dmitry A. Yashunin. "Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs." IEEE transactions on pattern analysis and machine intelligence 42.4 (2018): 824-836. ↩ Jayaram Subramanya, Suhas, et al. "Diskann: Fast accurate billion-point nearest neighbor search on a single node." Advances in Neural Information Processing Systems 32 (2019). ↩ Fu, Cong, et al. "Fast approximate nearest neighbor search with the navigating spreading-out graph." arXiv preprint arXiv:1707.00143 (2017). ↩ Jegou, Herve, Matthijs Douze, and Cordelia Schmid. "Product quantization for nearest neighbor search." IEEE transactions on pattern analysis and machine intelligence 33.1 (2010): 117-128. ↩ Singh, Aditi, et al. "Freshdiskann: A fast and accurate graph-based ann index for streaming similarity search." arXiv preprint arXiv:2105.09613 (2021). ↩ Gollapudi, Siddharth, et al. "Filtered-diskann: Graph algorithms for approximate nearest neighbor search with filters." Proceedings of the ACM Web Conference 2023. 2023. ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 21日目の記事です。 本記事では、メンバーそれぞれの個性が強すぎるが故に衝突して空中分解寸前だったチームの状態と、それを解決するツールとして 「わたしたちのウェルビーイングカード」 を活用し、チームの相互理解が進んだ話をしていきたいと思います。 はじめに こんにちは。イノベーションセンター IOWN推進室のつかごし( @22nu_n )です。昨年に続き、今年も12/21を担当します。 昨年のアドベントカレンダー では、イノベーションセンターのValuesである「『枠』を越えよう」にちなみ、提案資料改善を通して、個人としての視点だけに頼らず、チームとして複数の視点を持って新たな価値観を得たことで、“わかりやすく伝える”ことにつながったというお話をしました。 イノベーションセンターのValuesは残り2つの要素からなります。今年はそのうちの「Implement First」にちなみ、チーム内での衝突や食い違いが起きるフェーズをどうしのいだか、課題意識を持っていたチームの状態に対し、自分なりに仮説を立てて、まずは行動をしてみたという経験を綴りたいと思います。新規事業開発組織に属し、同様の状況下にある方の励みや一助になれば幸いです。 なお、内容は個人の見解にもとづくものであり、所属組織の総意ではないことをご了承ください。 チームに訪れた“混乱期” 組織が結成されて1年。メンバーはある程度揃いプロジェクトを推進する体制が整った!みんなで頑張っていくぞ!と思っていたのに…… 意見は対立するし、認識は食い違うし、ミーティングはいつも延長戦に突入する、あちこちで不満の声が出ている。そうこうしていたら、プロジェクトが遅延している!あのプロジェクトでも、このプロジェクトでも…… 例えば、Aさんが必要だと思っているプロセスに対して、Bさんは不要だと思っているため、協力体制を築くことができず、議論は平行線を辿り、プロジェクトの進捗は遅々として進みませんでした。 どうしてこのような状況に陥ってしまったのか。原因を探ってみました。 いくつか原因はありましたが、特に以下の3点がボトルネックになっていました。 メンバーが忙しい:他部署と兼務している方が多く、結果としてそれぞれの部署での打ち合わせ予定とこちらの部署での打ち合わせ日程が重なってしまったり、それによって打ち合わせに最初から最後までフル参加が難しいことが増えていた チーム体制の頻繁な変更:プロジェクトの状況に応じて、メンバーの入れ替わりが頻繁に発生していた 信頼関係の構築不足:さまざまな組織から人を集めたにもかかわらず、勤務形態がフルリモートで顔を合わせる機会が少なく、信頼関係を築く時間が不足していた 心理学の知見を借りれば、これは「タックマンモデル」の「混乱期」に当てはまります。 「タックマンモデル」とは、心理学者のブルース・W・タックマンによって提唱されたチームビルディングに関する考え方で、組織は体制を整えただけで完了ではなく、さまざまな問題の解決を行って理想の組織にしていく必要があると説いています。 形成期、混乱期、統一期、機能期、散会期の5段階を辿るのですが、「混乱期」ではメンバー間の対立や混乱が生じやすい時期とされ、まさに私たちのチームはこの渦中にあるといえました。 相互理解を深める機会の必要性 このような「混乱期」を乗り越えるために、まずはチームの相互理解を深める機会が必要なのではないかと思いました。 例えば、物事がうまく進まない時、足を引っ張っていると感じた人を糾弾したくなってしまうことがあります。しかし、“足を引っ張っている”と感じた行動は、その人の価値観や経験によって醸成された固有な考えに基づいています。そのため、チームとして仕事をするためには、お互いの価値観や経験を共有し尊重していくことが大切です。 お互いの価値観や経験を共有する手段があれば、円滑なコミュニケーションや共通認識の形成を図ることができ、今の状況から脱することができるのではないかという仮説を持っていたところ、 偶然にも「わたしたちのウェルビーイングカード」を使う機会に恵まれたので実践してみることにしました。 「わたしたちのウェルビーイングカード」を用いたチームビルディング 「わたしたちのウェルビーイングカード」 は、NTT社会情報研究所 well-being研究プロジェクトで制作された“自身や周囲の人々のウェルビーイングに意識を向け、対話をうながすツール”です。 これにより、気軽な相互理解の場を持つことができ、各自が秘めていた大切にしていることを認識できます。 今回は、“自分にとって大切なことを3つ選び、それぞれ選んだ理由やエピソードを共有する”という使い方をしました。 ある方は「挑戦」という言葉を挙げ、「常に新しいことに挑戦したい」と語り、一方で、別の方は「社会貢献」という言葉を選び、「社会貢献につながる活動をして誰かの役に立ちたい」と話したり… また、力強いリーダシップを大切にしていると感じていた方が実は「平和」を大切にしていたり… 全く違うタイプだと思っていた方々が実はほとんど同じ言葉を選んでいたり… 発表するたびにどよめきが起き、活発な対話が生まれました。 このように、普段同じチームで働いていても仕事をする上で大切にしていることは異なります。しかも、個人が最も大切だと思っていることを、周囲はほとんどわかっていないことがわかりました。 これでは、相手を思いやって発言・行動したつもりが、かえって相手の価値観を否定していたということもありえます。このような状態が日常頻繁に起こっている可能性が高いことを、発表の時の皆さんの「どよめき」が示しているのではないかと考えています。 これまでのことを思い返してみると、とある方がさまざまなアドバイスをかけてくださっていたのですが、私はそのアドバイスが理解できず苦しいといった状況に陥ったことがありました。 当時は「あの人は私のことを考えて相談に乗ってくれているのに、何故私は素直に受け止められないのだろうか?」と悩みましたが、今回の経験で自分の価値観と相手の価値観が全く異なることが根本的な原因だったのではないかと気づきました。 相手にとってよいとされる状態が私にとってはそうではなかっただけで、お互いに価値観の共有ができたら寄り添い協働していくことができるのだと実感しました。 メンバーの心の根底にある大切にしていることを、外から観察・類推するのではなく、本人から語る機会を作り、認識することで、チームメンバー一人ひとりの心を繋ぎ、協働して行けるのではないかと思います。 効果や反響 まだ全ての物事がスムーズに進んでいると言える状況ではないですが、最悪の膠着状態からは脱することができたと思います。 「このように対面で顔を合わせてお互いの価値について話す時間はとても良かった」とメンバーから大変好評でした。 この経験を経て、メンバー同士が相手の価値観を理解し、互いの価値観を尊重することで、チームが前に進むことに気づいた人が増えていたら嬉しいです。 今後に向けて チームで抱える課題を解決することは並大抵のことではありません。今後もチームの相互理解を継続的に行い、これからも次々に来るであろう「混乱期」の波を、みんなで乗りこなしていきたいと考えています。 いつか「あの期間があったから、チームとして強くなれた」と振り返る時がくることを願い、これからも様々なことを試し、よりよいチームの状態を追求してみたいです。 おわりに 今回は「わたしたちのウェルビーイングカード」を活用したワークを通して、各々の「ウェルビーイング」を共有し対話をしたことで相互理解が進み、チームが前進するきっかけになったというお話をしました。 「混乱期」のメンバー間の対立や混乱は、メンバー間で価値観の共有ができておらず、協働体制が構築できていないことに起因します。メンバーそれぞれ価値観が違うということを前提に仕事を進めていくことが大切です。 自分自身だけでなくチームメンバーの「ウェルビーイング」に目を向け、チームとして最大限の成果を上げていきましょう。 最後まで、ご覧頂きありがとうございました!それでは、明日の記事もお楽しみに!
アバター
この記事は、 NTTコミュニケーションズ Advent Calendar 2024 20日目の記事です。 国産OT-IDSであるOsecTの新機能、自動遮断機能についてご紹介します。 はじめに OsecTとは OsecTの役割 AMF-SEC連携機能の概要 不審な通信を検知した場合の自動遮断の流れ 開発の背景と今後の展望 アライドテレシス社との協業について おわりに はじめに こんにちは、イノベーションセンターの石禾(GitHub: rhisawa )です。 このたび、OsecTの新機能として、自動遮断機能を2024年12月にリリースします。今回はこの自動遮断機能と、NTT Comとアライドテレシス社との協業についてご紹介します。 OsecTとは OsecTとは、工場、プラント、ビル、船舶、社会インフラなどの制御機器を運用するOTシステム(OT; Operational Technology)のセキュリティリスクを可視化・検知するサービスです。 多様化するOTシステムのセキュリティ脅威に対して、パケット解析するセンサー機器を設置するだけで、OTシステムへの影響なく、ネットワークの可視化と脅威・脆弱性検知ができます。早期にリスク感知できる状態を作り、工場停止による損失を未然に防ぐことができます。 詳しくは過去のブログ記事に書いているので、興味がある人は是非ご覧ください。( OsecTリリース ・ OsecT前編 ・ OsecT後編 ) OsecTの役割 OTシステムのセキュリティ対策は基本、機器の接続構成などを明確化する「可視化」、異常を認識する「検知」、検知された異常に対応する「対処」からなります。これまで、OsecTはOT環境のデータを収集・蓄積・分析することで、可視化と検知の機能を提供するものでした。(参考: OsecT提供開始ニュースリリース ) そこに、「対処」に値する自動遮断機能ができたことで、一連のセキュリティ対策を講じることが可能となり、対応範囲が一段と広がりました。 AMF-SEC連携機能の概要 自動遮断機能は、アライドテレシス社AMF-SECとの連携により実現します。 AMF-SEC連携機能を使用すると、不審な通信をOsecTで検知した際、AMF-SECによって通信の自動遮断ができるようになります。アライドテレシス社は、AMF(Autonomous Management Framework)-SECを通じてネットワーク全体の一元管理を提供しています。AMF-SECによって提供される通信の自動制御機能を使用して、不審な通信を遮断します。 不審な通信を検知した場合の自動遮断の流れ AMF-SEC連携機能による自動遮断の流れは次のとおりです。 攻撃を受ける OsecTが不審な通信を検知する OsecTが検知メールを送信する 検知メールを受信する AMF-SECがメール記載の不審な通信を自動遮断する 1 通信遮断は、不審な通信のIPアドレスもしくはMACアドレスをトリガーとして行われます。IPアドレスもしくはMACアドレスの情報が含まれたメールをSyslogに変換することによって、AMF-SECが不審な通信を遮断できます。 開発の背景と今後の展望 ITにおけるセキュリティ対策として一般的に実施されている自動遮断は、OTにおいては誤遮断によるシステムの誤作動・誤停止への懸念から、これまで避けられてきました。背景には、ITとOTでセキュリティの三要素「機密性」「完全性」「可用性」の優先順位の違いがあります。 ITにおいては、個人情報や企業の機密情報などのデータが漏洩しないことを重視することから、機密性が最重要とされますが、OTにおいては、設備が安全に安定して動作し続けることを重視することから、可用性が最重要とされています。このように考え方が違うため、セキュリティ対策のアプローチも、ITとOTによって異なってきます。 しかし、ITとOTが融合する環境の増加に伴い、近い将来OTの一部領域においては自動遮断による対処を実施せざる得ない状況が出てくると考えています。また、医療系では完全性が重視されるなど分野ごとの特色に対応する必要があります。こうした考えから、OsecTは自動遮断機能を開発しました。 この新機能はOsecTにとって初めての他社連携機能となります。これからOsecTは、市場ニーズに応じてさまざまなネットワーク製品やセキュリティ製品と連携を図り、更なる展開を目指してまいります。 アライドテレシス社との協業について NTT Comは、アライドテレシス社と産業サイバーセキュリティビジネスの協業を開始しました。今回ご紹介したOsecT AMF-SEC連携 自動遮断機能は、多岐に渡る協業内容の1つです。 これまで、アライドテレシス社は、ITインフラ向けのネットワーク製品やセキュリティソリューションを提供してきました。一方、NTT Comは制御システム向けのセキュリティ対策「WideAngle OsecT」を展開し、ネットワークの可視化や脅威検知を行っています。 今回、NTT Comとアライドテレシス社が協業し、両社の製品を連携させた産業向けサイバーセキュリティ対策機能を開発・提供します。この連携により、「WideAngle OsecT」の再販や機能統合を進め、社会インフラ、医療分野、中小製造業や海外拠点工場へのセキュリティソリューション普及を目指します。 ニュースリリースについては こちら をご覧ください。 おわりに 今回は、国産OT-IDSであるOsecTの自動遮断機能を紹介しました。OsecT 自動遮断機能は、NTT Comとアライドテレシス社との協業にて実現されています。 本記事の内容が、ご検討のお役に立ちましたら幸いです。 ご興味がある企業さまは、 こちら からお問い合わせをお願いいたします。直販のお問い合わせだけでなく、販売パートナーも募集中です。 お問い合わせお待ちしております。 本システム連携にはアライドテレシス社の特許技術を使用 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 の記事です。 この記事では、OSINT(Open Source Intelligence)の基本的な考え方と、分析の際に重要となる認知バイアスへの対処方法について解説していきます。 また、実際の分析で使われる競合仮説分析(ACH: Analysis of Competing Hypotheses)という手法についても紹介します。 はじめに OSINTとは Intelligenceとは 本記事の目的 OSINTを成功させるためのマインドセット OSINTの特徴 マインドセットの基本原則 認知バイアスとは OSINTにおける認知バイアスのよくある例 認知バイアスを放置するリスク 認知バイアスを取り除くための手法:競合仮説分析 競合仮説分析の基本概要 手法のステップ まとめ こんにちは。イノベーションセンターの竹﨑( @z4ck_key )です。普段はNetwork Analytics for Security PJ 1 (通称NA4Sec)というチームで脅威インテリジェンスの分析業務を行っています。 この記事では、脅威インテリジェンス業務のみならず使えるOSINT(Open Source Intelligence)の基本的な考え方と、分析の際に重要となる認知バイアスへの対処方法について解説していきます。 また、実際の分析で使われる競合仮説分析(ACH: Analysis of Competing Hypotheses)という手法についても紹介します。 はじめに OSINTとは OSINTとは、 O pen S ource INT elligenceの略で、一般に公開されている情報を収集し、アナリストが分析することで生まれる成果物や一連のプロセスを指す専門用語です。 インターネット上のウェブサイト、ソーシャルメディア、公的文書など、誰でもアクセス可能な情報源を活用して、必要な情報を体系的に収集・分析します。 これは元を辿ると軍事情報機関で使用されていた手法でしたが、現在ではビジネスインテリジェンスや脅威分析など、さまざまな分野で活用されています。 Intelligenceとは 日本語で「情報」と訳されがちな言葉に、 Data , Information , Intelligence がありますが、この3つはそれぞれ指し示す意味が微妙に異なります。 Data ただそこにあるだけの値。 e.g.) 「ある組織のウェブサイトのアクセス数が1日1000件」という数値。 Information dataから読み取れる事象。 e.g.) 先月と比べてアクセス数が30%増加している。 Intelligence 分析や解釈を経て意思決定に活用できる形へ加工された知見。 e.g.) 競合他社の新製品発表に伴う市場の関心の高まりがアクセス数増加の要因として考えられ、今後の対策が必要。 このように、Intelligenceは生のデータや情報を分析・解釈し、実際のアクションにつなげられる形(これを Actionable と言ったりします)に昇華させたものと言えます。 OSINTは前述の通りOpen Source INTelligence なので、ただ情報を集めるだけではなくIntelligenceに整形して集めた情報をActionableにする必要があります。 本記事の目的 最近、OSINTという単語がセキュリティを業務にしている人やCTFに参加している人、およびその人達のSNSなどでよく見られるようになっています。 それ自体は喜ばしい一方で、OSINTにおける分析プロセスやマインドセットが触れられず具体的な方法論ばかりが注目されがちだと感じます。 本記事では、OSINTを実施する上で大切なマインドセットや、 分析の際問題になりがちな認知バイアスへの付き合い方について言及します。 それ故に、この記事内で具体的な情報取得のHow toは触れません。ご承知おきください。 OSINTを成功させるためのマインドセット OSINTの特徴 OSINTは前述の通り公開されている情報を収集することが分析の起点です。 それ故に、情報源には多様性があり、質のばらつきも存在することが前提となります。 取得できた情報全てが正しいと 確信を持てることは少ない ということです。 このような特徴を踏まえ、OSINTを効果的に実施するためには、 分析者の主観や偏見を排除 し、 客観的な視点を維持する ことが極めて重要です。 そのため、以下のようなマインドセットを持つことが必要不可欠です。 マインドセットの基本原則 先入観を持たないこと 収集時に自分の仮説を押し付けない 異なる視点や情報に対してオープンな姿勢を保つ 情報の信憑性を常に疑うこと 取得した情報が全て正しいと思い込まない 情報源の信頼性を評価するための基準を持つ これらの原則を意識することで、予想外の情報を発見したり、偽誤情報や古い情報に直面した際にも、冷静に対応できる可能性が高まります。 しかし、どれだけ先入観を排除し、情報の信憑性を疑うことを意識しても、私たちの思考には無意識の偏りが存在します。 次に、その偏りを引き起こす「認知バイアス」とは何かについて見ていきましょう。 認知バイアスとは 認知バイアスとは、人間の思考や判断に影響を与える心理的な傾向や偏りのことを指します。 これは私たちの脳が情報処理を効率化しようとする過程で自然に生じる現象であり、特にOSINTのような情報分析において大きな課題となります。 分析者は自身の認知バイアスを理解し、それを意識的に制御することが重要です。 OSINTにおける認知バイアスのよくある例 情報を収集している中で、よく直面しがちな認知バイアスの例について、いくつか紹介します。 例1: 確証バイアス 自分の仮説に合致する情報だけを集めてしまうバイアスのこと。 例えば、「調査中のAPTは中国のグループだ」という仮説を立てた場合、その仮説を裏付ける情報ばかりを重視し、反証となりうる情報を無意識的に無視してしまったりするのがこの例。 例2: アンカリング効果 最初に得た情報がその後の分析に過度に影響を与える心理現象のこと。 人間の脳は新しい情報を処理する際、既知の情報を基準点として利用する傾向を持つため、これが原因とされている。 認知バイアスを放置するリスク これらの認知バイアスを放置して分析を進めることは、インテリジェンスの正確性が信用できなくなり、誤解にもとづく意思決定の可能性が生まれてしまうことにつながります。 自分がより主張したい情報だけを集めたり、都合のいいように情報を解釈して意思決定してしまう経験は皆さんにもあることでしょう。 OSINTは分析した結果をもとに次のアクションに繋げるためのものなので、誤解にもとづく意思決定をするということは意思決定者にとって喜ばしくない結果を引き起こすことになります。 認知バイアスを取り除くための手法:競合仮説分析 前述した認知バイアスの影響をなるべく最小限に抑え、複数の可能性を体系的に評価するためのメソッドとして、 競合仮説分析 (ACH: Analysis of Competing Hypotheses) が存在します。 本節では、競合仮説分析の概要とその手法について説明します。 競合仮説分析の基本概要 競合仮説分析は諜報機関で使われていた手法であり、アメリカ国防総省の情報機関CIAの情報分析官リチャーズ・J・ホイヤーによって広く啓蒙されました。その時の書籍はCIAによって一般に 公開されている ので、興味があればぜひ見てみてください。 この手法の最大の特徴は、 仮説を1つずつ検討するのではなく、複数の仮説を同時に評価するプロセスを採用している点 です。 単一の結論に急いで飛びつくのではなくあらゆる可能性を慎重に検討することで、認知バイアスを除去し特定の仮説へ偏った結論に至るリスクを軽減します。 分析官は自身の直感や先入観に頼るのではなく、証拠に基づいて各仮説を公平に評価することが求められます。 筆者の周辺では、脅威インテリジェンス (CTI: Cyber Threat Intelligence) を生成する目的で行う分析において、実際に使用されることも多いです。 手法のステップ 競合仮説分析は以下のプロセスに分割して定義されています。 仮説列挙 可能性のある仮説をできるだけ多く列挙します。 仮説の列挙は個人で実施するとこの時点でバイアスに呑まれてしまう可能性が存在するので、本来は色々な人の意見を募って実施するべきとされています。 現代においては生成AIの助けを借りることで、個人であっても比較的幅広い仮説を列挙できるでしょう。 証拠集め 各仮説を裏付けたり反証したりするための証拠を収集します。 OSINT文脈においては公開情報から証拠を収集しますが、インテリジェンス分析の際にはOSINTに限らず他の情報源も活用して情報を収集できるとより望ましいでしょう。 比較 仮説列挙フェーズで列挙したそれぞれの仮説に対して、証拠集めフェーズで収集した証拠がどのように寄与するかをマトリックスにして分析します。 今回は、過去に自分が作成したマトリックスを例に挙げます。 作成したマトリックスは以下のようになり、この作業でどの証拠がどの仮説に寄与するかの関係性が可視化されます。 この例では、 + :仮説を支持する、 - :仮説を反証する、 0 :どちらでもない、という表記で仮説を評価しています。 再評価 比較フェーズで一旦評価したマトリックスを、以下を実施して改良します。 精査の結果、新しい仮説や証拠があれば追加。 裏付ける証拠がない仮説を削除。 これにより、関連性の薄いと判断できる仮説の棄却と不足している証拠を他の仮説への寄与度込みで評価できます。 仮説選定 再評価フェーズで絞り込まれた仮説の中から、最も多くの証拠によって支持される仮説を選定します。 反証可能性の検証 このフェーズが競合仮説分析の考え方においては重要なフェーズになります。 仮説選定のフェーズで絞り込んだ仮説に対して、それぞれの仮説がどの程度特定の証拠に依存するかを以下の観点で確認します。 主要な仮説は 1つか少数の証拠に依存しているか 重要な証拠が正確だという確信度はどの程度あるか 証拠は時間の経過とともに変化する可能性がある事象か。変化する場合は仮説にどのような影響を与えるか。 このフェーズを踏むことにより、分析の段階で混入した主観的なバイアスを取り除くきっかけが生まれます。 レポーティング 最終的に意思決定者にこれらの分析結果を報告する必要があるので、レポーティングをする必要があります。 この時に注意すべきことは、主となる仮説を裏付けに使用した証拠とともに提示し、それを 推定的な表現 を用いて報告することです(あくまでもこの分析結果はfactではなく、一番確からしい仮説なため)。 最後に評価を補強するために検討した他の仮説や分析過程も提示することで、意思決定者が結論の背後にある理由を理解できるようになります。 まとめ OSINTを成功させるためには、単に情報を収集するだけでは不十分です。次のアクションに繋がる形で情報を加工し、価値あるIntelligenceに昇華させる必要があります。 そのプロセスの中で重要なのは、分析者自身が持つ認知バイアスを理解し、それを可能な限り排除することです。認知バイアスが混入した分析は、誤った結論を導き、意思決定を誤らせるリスクを孕んでいます。 本記事で紹介した 競合仮説分析(ACH) は、認知バイアスを克服し、客観性を高めるための有効な手法です。この手法を用いることで、複数の仮説を公平に評価し、最も支持される仮説を導き出すプロセスが体系化されます。 OSINTは、日々変化する複雑な情報環境の中で、情報を正しく理解し真偽を見分ける為に、専門家だけでは無く個々人にとってもますます重要なスキルとなってくるでしょう。 本記事を通じて、OSINTの実践における心構えや具体的な手法について理解を深めていただけたなら幸いです。 最後に、OSINTは「情報の力」を活用するためのツールです。適切なマインドセットを持ち、分析の質を高める努力を惜しまないことで、その力を最大限に引き出すことができるでしょう。 というわけで、 NTT Communications Advent Calendar 2024 5日目の記事でした。 明日もお楽しみに。 NA4Secプロジェクトについては、このブログの記事  サイバー脅威インテリジェンス(CTI)配信はじめました  をご覧ください。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 及び ProtoPedia Advent Calendar 2024 の19日目の記事です。 こんにちは、AIロボット部(社内サークル)部員の宮岸( @daiking1756 )です。 普段は5GI部で映像系商材のプリセールス的なお仕事をしています。 最近急に寒くなってきましたが、昨年から使い始めた アームウォーマー によって、タイピングに影響することなく腕を温めながら生活しています。 部屋全体を温めると頭がボーっとして眠くなるので、暖房は弱めに設定するのが好みなのです。 電熱線などが入っているものもありますが、私の場合どうしても手に違和感があって、タイピングしにくくなるので、このタイプに落ち着きました。 さて、本記事では 3日目の記事 では触れなかったロボット部の活動の紹介として、 ヒーローズリーグ(旧: MashupAward) という開発コンテストの20回記念の展示会にロボット部として参加した話をまとめます。 はじめに ヒーローズリーグ(旧: Mashup Awards)とは MA/HL20thイベントでやったこと ロボットカフェについて temiのセッティング 当日ロボットカフェを利用した方の声を紹介 展示した作品について おわりに はじめに タイトルの通り、イベントでロボットカフェと作品展示をやったよーという記事なのですが、 イベント自体の紹介などもしつつ前提から書いていきたいと思います。 ヒーローズリーグ(旧: Mashup Awards)とは Wikipedia では下記のように説明されています。 Mash up Awards(マッシュアップアワード)は、2006年にスタートしたWeb開発者が自ら開発したWebサイトやスマートフォンのアプリケーション等の技術、デザイン、アイデアを競い合うコンテストである。略称 MA。 2006年から2018年までMashup Awards、2019年以降はヒーローズリーグと改称して開催されている。 上記の通り、Mashup Awards時代に12年、ヒーローズリーグになってからは今年で6年目。 その長い歴史と、作品応募数の多さ(最大500 over, 今年も300 over)から、日本最大級の開発コンテストと呼ばれることもあるイベントです。 Mashup Awards時代の歴史については 「この技術いつ流行った?」という観点で運営の方がまとめてくださっています。 また、先日行われた 【20回記念イベント】 #ヒーローズリーグ & #MashupAwards を振り返る のYouTubeLiveで私も少し喋りましたが、 これまでの20回の大会の軌跡がまとまっています。 私自身は2018年に初めて参加し、そこから毎年何かしらの作品を応募しています。 特にコロナ前に行われた2018年、2019年の決勝戦の熱気は今でも忘れることができず、 確実に私がメイカー活動を始めた強烈なきっかけとなったイベントになりました。 今年は20回目の記念大会ということで、 コロナ明けぶりに オフラインイベント(決勝配信 & 展示会) が12/7(土)に開催されたのでした。(以下、「MA/HL20thイベント」 とします) MA/HL20thイベントでやったこと ドコモがメインスポンサーを務めていることもあり、 会場として docomo R&D OPEN LAB ODAIBA の提供、 賞の提供 、 スポンサーLT の3点で関わりました。 また、NTT Comのロボット部としては普段作成しているロボットの展示と、ロボットカフェの運営を行いました。 ロボットカフェについて ロボット部では普段部員が自由にロボットやIoT関連の作品を制作しています。 その活動の一環として、今年6月にNTTグループ内でモノづくりを行っている有志での社内イベントにて、ロボットカフェの取り組みを行いました。 ロボットカフェとは、 temi という自律走行型パーソナルロボットがコーヒーを運んでくれるカフェのことです。 お客さんはカウンターでコーヒーを注文し座席に座ると、temiにゃん(今回はtemiのセリフにネコ属性を付与しています)が一生懸命コーヒーを座席まで運んでくれます。 コーヒーは社内でも有名な?バリスタの方が拘りのコーヒーを一杯ずつ丁寧に注いでくれました。 (週末は 江東区のお店 に立ってコーヒーを淹れているそう) 今回は6月に社内向けに実施したロボットカフェの取り組みの一部をバージョンアップして、一般向けのイベントで実施しました。 temiのセッティング temiは Buddiotte という専用のソフトウェアを使うことで、ブロックプログラミングで制御できます。 「フロアのマッピング」や「指定した座席まで移動し、定位置まで戻ってくる」という単純な動きであれば、temiの標準機能で実現できます。 画像の赤枠エリアは禁止エリア(仮想壁)としてtemiが立ち入らないようになっています。 今回は机に当たらないように注意しながら経路を設定しました。 今回はBuddiotteを利用することで、挨拶をしながらドリンクを運ぶであったり、指定した座席へコーヒーを運んだ後にユーザのタッチを起因に待機場所まで戻ってくるなどの複雑な動きを簡単に実装できました。 フロアのマッピングをしている様子は撮影し忘れてしまったのですが、マッピングをする際には追従モードを利用します。 追従モードでは先行して歩く人の後ろをtemiが追従する形で自走をします。 temiは追従をしながらLIDARセンサによってフロアの構造物・障害物を認識することでマッピングが実施されるという流れです。 当日ロボットカフェを利用した方の声を紹介 当日は約50人ほどの方にコーヒーを提供しました。 コーヒーの味はもちろん、ロボットの愛くるしい仕草で皆さんを笑顔にしてくれました。 A1:ロボットカフェさん コーヒーをいただいた ドンピシャで座った席に来てくれた 帰る時だけにゃんつけるのがあざとい? #ヒーローズリーグ pic.twitter.com/w0wFUtTLwO — パスコンパス (@pscmps) 2024年12月7日 x.com そして当日は配信室にもコーヒーをお届けしましたよ。 その時の様子はこちら。 www.youtube.com (また、同YouTubeLiveの 5:57:38~ はスポンサーLTとして5分ほど喋りました) 展示した作品について ロボット部のブースで展示した作品は下記の4つです。 懐中電灯(偏光)で操作するロボット (詳細は今年のアドカレ 3日目の記事 ) 電気ネコ 高専かるた (詳細は昨年のアドカレ 7日目の記事 ) HANAPIKU - Awesome Nose Interface - ロボット部の設営完了! お時間ある方はぜひお台場まで遊びに来て下さい〜🙌 #ヒーローズリーグ pic.twitter.com/xnhc3TcPc5 — みやぎdaiking⊿🌗 (@daiking1756) 2024年12月7日 x.com それ以外の展示作品の情報は ProtoPediaのイベントページ にまとまっています。 当日は約40個の作品が展示され、会場の熱気は、コロナ前の決勝戦を彷彿させるものでした🔥 特に展示者同士の「これ裏側どうなってるの?」「そのセンサの使い方は考えつかなかった!」などとコミュニケーションが弾むあの時間は、 何にも代えられないほど楽しい時間だな改めて感じました。 特に懐中電灯ロボットの偏光板の使い方には、皆さん興味を持って頂いたようで、 作成したメンバーは楽しそうに仕組みを説明していました。 C1: @ daiking1756さん 光の向きで操縦するマイコンレスカーがすごい! 偏光板を使うことでCdsの抵抗値のバランスを崩すことでモータを駆動させている仕組み 目から鱗でかなりちゃんと操縦できることに驚き #ヒーローズリーグ pic.twitter.com/e3JRx6pGfA — パスコンパス (@pscmps) 2024年12月7日 x.com ブラウザから操作できる猫型ロボット 操作画面のアニメーションが3Dモデルをフラットに表現しているの好き #ヒーローズリーグ pic.twitter.com/IXZO0bpCfn — 妄想発明家ZAWAWORKS (@zawa_works) 2024年12月7日 x.com その他、イベント当日の様子はXにて #ヒーローズリーグ で検索するとたくさんヒットするので、ぜひご覧ください。 おわりに ロボット部の部員仲間と本イベントに参加できたことがとても良い経験になりました。 参加したロボット部の部員からも下記のような感想が挙がりました。 技術イベントに参加するのがご無沙汰だったのでとても刺激になった。久々に何か私的に作ってみたい! 個性的な作品はもちろん、作った人の話を直接聞けるのがとても面白かった。ロボットカフェもたくさんの人に楽しんでもらえて良かった。 さまざまな発想の作品を見たり聞いたりできて楽しかった。自分も自由な発想でモノづくりを楽しんでいきたいと思った。 社内サークルって、活動内容だけでなく、色んな人が持っている技術や文化やコミュニティが入り混じる、 いわゆる 越境 が面白さの醍醐味だと思いますが、 今回はそれを存分に味わえた気がしています。 今後も社内外のイベントで作った作品をドシドシ展示していきます🙌 また、本イベントの準備は3月頃からスタートしましたが、過去の経験に加えて、 ドコモアカデミー という 半年間の社内研修を終えた直後でフッ軽なマインドになれていたことも、今回の取り組みに結びついたと実感しています。 私自身はさまざまなバックグラウンドの人とわちゃわちゃするのが好きなので、越境による化学反応を楽しみつつ、これからも仕事に励んでいきます。 最後に、自分のキャリアや人生に大きな影響を与えたMA/HLというイベントに、今回会社として関われたことが何より幸せでした。 それでは明日の記事もお楽しみに〜👋
アバター
この記事は、  NTT Communications Advent Calendar 2024  17日目の記事です。 みなさんこんにちは、イノベーションセンターの平木です。 Network Analytics for Security 1 (以下、NA4Sec)プロジェクトのメンバーとして活動しています。 この記事では、利用終了ドメイン名観測の環境構築の知見を共有していきます。 明日の記事では観測されたログの分析結果を紹介したいと思います。 利用終了ドメイン名観測分析施策の背景と目的 工夫した点 DNSクエリのみならずWebアクセスログも取得する パブリッククラウドとマネージドサービスを利用する 構成 大量アクセスを捌くコツ コスト まとめ 利用終了ドメイン名観測分析施策の背景と目的 昨今、廃止したドメイン名がドロップキャッチ 2 され被害にあうケースが多発しています。 NTTドコモグループでも、過去にドコモ口座で使用していたドメイン名が廃止後にオークションにかけられた事案がありました 3 。 ドロップキャッチされることで、フィッシングサイトなどで悪用されるリスクがあります。そこで、これらドロップキャッチの被害を最小限に抑えるために、NTTコミュニケーションズでは利用終了したドメイン名を永年保有する方針で運用を開始しました。 ただ一方で、組織が使用していないドメイン名を保有し続けることはインターネットの健全性に悪影響を及ぼす可能性があります。 そこで、利用終了ドメイン名の観測を通し、実際にどのような通信を受けているのか、そしていつ手放すことができるのか、また手放すために通信を減らすことはできないかなどを検討すべく、2024年に利用終了ドメイン名の観測分析の施策を立ち上げる運びとなりました。 工夫した点 DNSクエリのみならずWebアクセスログも取得する ドメイン名の観測のため、当然DNSクエリログを収集しますが、同時にWebアクセスログも収集することにしました。 Webアクセスログも収集するようにした理由は、アクセス元やアクセス用途を特定し、より効果的な対策につなげるためです。 DNSクエリログは情報量が少なく、それ単体では誰がどこから何の用途でアクセスしてきたかを推し量ることは困難です。 そこでDNSクエリ結果の主たる用途の1つとして考えられるWebアクセスに着目し、WebサーバへのHTTP/HTTPSアクセスも収集できる形を要件としました。 さらに、より幅広くWebアクセスログを収集するため、サブドメイン名も対象としました。 サブドメイン名は、Passive DNS 4 から取得したサブドメイン名を登録することにしました。 パブリッククラウドとマネージドサービスを利用する 今回AWSを使いやすい状況が整っていたこと、またコストを抑えすぐに利用できる点も勘案し、AWSで観測環境を構築することにしました。 一方、分析・ログ保管については、社内リソース有効活用の観点で全社データ基盤を採用しています。 また、運用の手間を減らしつつもセキュリティを担保できるよう、マネージドサービスを使うことにしました。 Amazon EC2などでサーバを立てて構築した場合に比べ、マネージドサービスの方がAWS側のセキュリティ対応範囲が広くなるため、運用の手間が減るというメリットがあります。 構成 構成の全体像は下記の通りです。 送信元として、サービスが終了したが消し忘れられたリンク(残存するリンク)や、消し忘れの監視通信(残存監視通信)、 そしてインターネット全体で定常的に発生している探索通信などを想定しています。 それら通信をAWSのAmazon Route53、Amazon CloudFrontで受け、ログをJSON化しています。 JSON化されたログは、全社データ基盤に保存され、全社データ基盤のJupyter Notebookを使って分析できるようになります。 なお、送信元に対しては「何もページを返さない」(Access Denied)形を取ることで、 Amazon CloudFrontの利用料金を月数十円以下と低く抑えて運用しています。 DNSクエリログ、Webアクセスログ処理の詳細な流れは以下の通りです。 Amazon Route 53がDNSクエリを受ける DNSクエリログがAmazon CloudWatch Logsに蓄積される(4.の処理の負荷分散のため、分散して蓄積) ログは1日1回、Amazon EventBridgeのCreateExportAPIにより、Amazon S3バケットにExportされる Exportされたタイミングで、AWS Lambda関数が立ち上げられ、JSON化される(AWS Lambdaの設定により、Amazon S3のファイル生成をトリガーに、AWS Lambdaを立ち上げている) 全社データ基盤がJSON化されたファイルを1日1回取得する Amazon CloudFrontがWebアクセスを受ける WebアクセスログがAmazon S3へ出力される(Amazon CloudFrontの設定により、ログをAmazon S3へ出力している) Amazon S3にログが生成されると、AWS Lambda関数が立ち上げられJSON化される(AWS Lambdaの設定により、Amazon S3のファイル生成をトリガーに、AWS Lambdaを立ち上げている) 全社データ基盤がJSON化されたファイルを1日1回取得する 大量アクセスを捌くコツ 今回の構成では、AWS Lambda関数の作りが最もキーになったと感じています。 当初は入力としてAWS Lambda関数1処理当たり数行〜数百行程度のデータを想定していましたが、観測するドメイン数が増えると、数万行のデータも現れ、タイムアウトやエラーが増えていきました。 これらタイムアウトやエラーに対応すべく試行錯誤を重ね、いくつかのAWSサービスを試したものの、最終的にはAWS Lambda関数(Pythonプログラム)の改良が、高速性やエラーの低減に最も効いたと感じています。 特にAWS Lambda関数を作るに当たっては「無駄な処理をなくすこと」が一番のポイントだったと考えています。 メモリに読み込むファイルは最小限にする(例えば全行は読み込まず、必要な数行のみ読み込む) ファイルをフィルタするにあたって、テキストのままフィルタする(JSON化後にvalueでフィルタしてしまうと、入力データが増えるにつれ重くなるため) コスト 全体のコストのうち、AWSの運用にかかるコストを紹介します。 2024年5月〜10月までのAWS利用料金を可視化したのが下図(左)、アクセス数を可視化したのが下図(右)となります。 下図(左)について、横軸が月、縦軸が月当たりのAWS利用料金となっていて、システムが安定しドメイン名が一定数(24)で推移した7〜10月の料金が安定していることが分かります。この時期の料金は概ね15000円前後となっています。 下図(右)について、横軸が月、縦軸がアクセス数となっていて、ドメイン名を増やしたタイミング(丸をつけた箇所)で、アクセス数が増えたことが分かります。 また、より詳細なコスト分析が以下の図です。 コストの主因はAWS Lambda、Amazon Route 53、Amazon S3であることが分かります。 AWS Lambdaについては、アルゴリズムが最適化されておらず金額が高くなっていました。 そこで、11月末にアルゴリズムを改善したところ、大幅な高速化に成功しています。 そのため、11月以降のAWS Lambdaによる支出は、大幅に低減される見込みです。 Amazon Route 53については、DNSレコード維持費が75%を占めています。 コストの内訳は、DNSZone75%、DNSQuery25%でした。 つまり、75%がDNSレコードの維持費用ということが分かります。 Amazon S3については、全社データ基盤の連携コストがほとんどを占めていると考えています。 コストの内訳は、API Request99.3%、Storage0.7%でした。 API Requestの中身は分かりませんが、全社データ基盤からのJSONファイルの取得によるものと考えています。 まとめ 利用終了ドメイン名の観測分析施策の、観測環境について振り返りました。 明日の記事では、この環境で収集したログの分析結果について紹介する予定です。お楽しみに。 NA4Secプロジェクトについては、このブログの記事  サイバー脅威インテリジェンス(CTI)配信はじめました  をご覧ください。 ↩ ドロップキャッチについては、JPNICの ドロップキャッチとは にて分かりやすく説明されています。 ↩ ドコモ口座の件については、ITmedia NEWSの 「ドコモ口座」のドメイン、ドコモが取り戻す 出品の経緯をGMO含め聞いた をご覧ください。 ↩ Passive DNSについては、エヌ・エフ・ラボラトリーズ株式会社のエンジニアブログ サブドメイン名列挙の方法についてまとめてみた にて分かりやすく説明されています。 ↩
アバター
この記事では、 NTT Communications Advent Calendar 2024 16 日目の記事です。本記事では MLflow という実験管理 OSS を Google Cloud の Vertex AI Experiments に置き換えを検討してみた話について記載しています。 はじめに 結論 話題の中心となる実験管理機能 浮き彫りになった課題 パフォーマンス面 セキュリティ面 Vertex AI Experiments によるアプローチ 検討の中でぶち当たった壁 前提知識の整理 テスト実行のメタデータの扱い方 API コールの上限 考察と今後 さいごに はじめに こんにちは、イノベーションセンターの林です。普段はノーコード AI 開発ツールである「 Node-AI 」というプロダクトでソフトウェアエンジニアとして開発に携わっています。また、SRE・オブザーバビリティ・Google Cloud といった領域も好きで趣味で勉強していたりします。 Node-AI は AI 開発ツールということもあり、アプリケーションの構成の中に機械学習系のタスクを担うコンポーネントも存在しています。その中で MLflow と連携しているエンドポイントがあり、その部分において運用続ける中で課題が浮き彫りになりました。 本記事では、この課題に対するアプローチとして同じく実験管理機能を提供する Google Cloud の Vertex AI Experiments に置き換える検討をしたことについてまとめていきます。 結論 簡単に本記事の結論をまとめると、 現時点での MLflow の利用方法では Vertex AI Experiments に置き換えるのは厳しいという結果でした 。 Google Cloud のリソースはものによって上限が割り当てられていてサービスの過度な負荷を防いでいます。Vertex AI Experiments というサービスにも実験情報の記録や取得について上限が割り当てられています。 検証の中で単純なユースケースにも関わらず、この上限に当たってしまいエラーが発生する事態に陥りました。上限は申請によって緩和できますが、実際のユースケースを想定したときに多少緩和しただけでは同様のエラーに陥ることが考えられました。 これは Node-AI の MLflow の利用の仕方が起因しており、 本来の想定されている利用の仕方から逸脱していることが根本原因だと考えています 。まずは、その点の見直しから整理していく必要があると結論に至りました。 以降では、実験管理機能の説明、 MLflow・Vertex AI Experiments の紹介、検証する中でぶち当たった壁、その壁に取り組んでみての考察と今後という流れで執筆しています。 話題の中心となる実験管理機能 前提知識として、実験管理について説明します。実験管理の目的は、実験 = モデルの評価状況を記録することで後から比較・モデル選定を可能とすることです。一般的に管理対象となるのは、下記の要素が挙げられるようです。 コードバージョン:どのようなコードで実験をしたか データバージョン:どのようなデータで学習・テストをしたか ハイパーパラメータ:ハイパーパラメータとして何を使用したか メトリクス:実験の結果、どのような結果が得られたか 環境:実際に動作したときのマシンとしてどのようなものを使用していたか つまるところ、実験を再現するための情報とその結果の記録が主要な機能と言えそうです。 この 実験管理機能を提供している代表的な OSS が MLflow となります。MLflow にはこの実験管理機能に加えて、記録した精度を比較しやすくする可視化機能、記録した情報に紐づくモデル保存機能なども提供しています。 引用: https://mlflow.org/docs/latest/getting-started/quickstart-2/index.html#compare-the-results 浮き彫りになった課題 Node-AI では上述した通り、機械学習系のタスクを担うコンポーネントが存在しており、一部のエンドポイントで MLflow と連携しています。連携方法としては、データベースに対する取得・保存のためのインターフェースとして MLflow の実験管理に関する API を利用しています。 この MLflow は導入当初問題なく利用できていたのですが、運用を続ける中でユーザー数の増加や学習規模の増大によって以下のような課題が顕著となりました。ここでは 2 つ取り上げたいと思います。 パフォーマンス面 Node-AI ではモデル作成時に複数回の学習を実行します。この複数回の学習結果をニアリアルタイムで取得し UI 上に描画したいがために、学習量ごとの保存に加えて 1 秒間に 3~4 回の取得の API コールをしています。 この利用の仕方では書き込み及び読み取りの処理が遅く、学習開始から完了までの一連のリクエストのボトルネックになっています。これは同時に学習を開始するユーザー数が多いほど、負荷は増大してパフォーマンスの劣化を引き起こします。 セキュリティ面 クリティカルなものとして、定期的に検出される脆弱性によって緊急のアップデート作業が必要となる点です。下記は、昨年末に検出された脆弱性の例です。 機械学習のライフサイクルを管理するプラットフォーム「MLflow」に3件の脆弱性が明らかとなった。いずれも「クリティカル(Critical)」とレーティングされている。 サーバ上にファイルを配置することが可能となるパストラバーサルの脆弱性「CVE-2023-6015」が判明。また認証なしにファイルを上書きし、リモートよりコマンドを実行してデータやモデルにアクセスが可能となる「CVE-2023-6018」が明らかとなった。 さらに「REST API」の認証をバイパスしてアカウントの作成が可能となる「CVE-2023-6014」が判明している。 CVE番号を採番した脆弱性情報報告サイトのhuntrでは、共通脆弱性評価システム「CVSSv3.1」のベーススコアを「CVE-2023-6015」「CVE-2023-6018」のいずれも最高値である「10」とした。また「CVE-2023-6014」は「9.1」と評価している。重要度はいずれも「クリティカル(Critical)」。 引用: https://www.security-next.com/151212 こういった側面に加えて、OSS 特有のセルフでのメンテナンスコストの高さも相まって運用の負荷を増大させる要因となっています。 Vertex AI Experiments によるアプローチ 私自身が Google Cloud を好んでいることと実験管理機能を提供しているという 2 点から Vertex AI Experiments を置き換えの検討候補としました。 Vertex AI Experiments に関する説明は、公式ドキュメントより引用します。 Vertex AI Experiments は、さまざまなモデル アーキテクチャ、ハイパーパラメータ、トレーニング環境を追跡および分析し、テストの実行の手順、入力、出力を追跡する際に役立つツールです。Vertex AI Experiments では、テストデータセットに対して、トレーニングの実行中に、モデルの集計のパフォーマンスを評価することもできます。この情報を使用して、特定のユースケースに最適なモデルを選択できます。 引用: https://cloud.google.com/vertex-ai/docs/experiments/intro-vertex-ai-experiments?hl=ja 上述した一般的な要素に加えて実行順序や関連したオブジェクトの紐付けといったところも可能になっているようです。 Vertex AI Experiments に期待した点としては、 Google マネージドなサービスというところでのセキュリティ面の課題からの解放およびコンピューティングスペックのオートスケーリングによるパフォーマンス面の課題からの解放という点 でした。(検討開始当初は、前者は間違いなく実現できると思っていましたが、後者は要検証項目となっていました。) 検討の中でぶち当たった壁 検討は大きく下記の手順で進めました。 Vertex AI Experiments の理解 現状の実装における MLflow の利用の仕方の理解 現状の実装の MLflow を Vertex AI Experiments で置き換えられるかの検証 また、検討過程は Architecture Decision Record(ADR) として GitHub Issue にまとめました。こちらの記事執筆にも非常に役立っています。 Vertex AI Experiments の理解 では、主に公式ドキュメントの読み込みと用意されているサンプルコードを触ったりでどういった特性があるのか、利用の仕方ができるのかなどを確認しました。サービスによるのですが、公式ドキュメントに Colab や Colab Enterprise ですぐに環境を立ち上げてサンプルコードを実行できるリンクが用意されていたりします。 引用: https://cloud.google.com/vertex-ai/docs/experiments/intro-vertex-ai-experiments?hl=ja 上図内の「Colab Enterprise で開く」をクリックすると、Google Cloud Console に遷移して Vertex AI の Colab Enterprise が開きます。 現状の実装における MLflow の利用の仕方の理解 では、自分自身今までに触ったことがないコンポーネントだっためチームのテックリードにコードを解説してもらいながら理解を進めました。 それらの手順を経て、 現状の実装の MLflow を Vertex AI Experiments で置き換えられるかの検証 に進みました。Vertex AI Experiments は MLflow と一部互換性があり、どの程度現状のコードを置き換えられるかという点にフォーカスして進めました。 そこでぶち当たった壁 = 課題について 2 点紹介します。 前提知識の整理 実際の課題について説明する前に単語の認識を合わせたいと思います。Vertex AI Experiments の公式ドキュメントを参考にします。 1 回の学習記録を 「テスト実行」 と呼びます。テスト実行にはその学習で設定したハイパーパラメータ・学習結果のメトリクス・その他の環境値などが記録されます。コード上では ExperimentRun というオブジェクトで表現されます。 テスト実行には、ユーザー定義の指標、パラメータ、実行、アーティファクト、Vertex リソース(たとえば、PipelineJob)を含めることができます。 引用: https://cloud.google.com/vertex-ai/docs/experiments/intro-vertex-ai-experiments?hl=ja#experimentrun また、上記のテスト実行の集合を 「テスト」 と呼びます。コード上では Experiment というオブジェクトで表現されます。 テストは、ユーザーが入力アーティファクトやハイパーパラメータなどのさまざまな構成をグループとして調査できるパイプライン実行に加えて、一連の n 個のテスト実行を含むことができるコンテキストです。 https://cloud.google.com/vertex-ai/docs/experiments/intro-vertex-ai-experiments?hl=ja#experiment テスト実行のメタデータの扱い方 最初の大きな課題となったのは、 テスト実験のメタデータの扱い方 です。Node-AI の実装の中では、特定のタイミングのテスト実験結果を取得して描画する画面が存在します。 学習方法はクロスバリデーションの有無やパラメータ探索の有無などいくつかの組み合わせが存在する上に、学習完了後にまとめて描画するのではなく ニアリアルタイムに描画していることから特定のタイミングの結果を取得する必要がありました 。これには、 テスト実行にメタデータを付与して高速に検索する ことで対応しています。 これを実現するために、MLflow の API の中でも set_tags() を利用しています。その他にも利用しているものもありますが、主だったものとして log_metrics() と log_params() というものがあります。それぞれの簡単な用途が下記になります。 log_metrics():学習毎の評価制度を記録する log_params():学習毎のパラメーターを記録する set_tags():学習毎のメタデータを記録する 上述した通り、Vertex AI Experiments は MLflow に一部互換性があり、これらの API を置き換えられると検証がスムーズに進むため大きなポイントでした。しかし、そんなに甘いことはなく テスト実行のメタデータを記録する set_tags() のみ実装されていませんでした。 この課題は、Vertex AI Experiments を扱う Python ライブラリである google-cloud-aiplatform がどのような構造になっているかを読み解くことで解決しました。 こちらにまとめるとボリュームが 1.5 倍になりそうなので、詳細は「 Vertex AI Experiments の実態 - コードを辿った先にあったもの - 」という資料と「 Vertex AI Experiments をコードから読み解いてみた 」というブログにまとめています。前者の資料はここで述べている課題にフォーカスした内容で登壇した際のものです。後者のブログは実際にどのようにコードを読み解いたのかという内容になっていますので、興味ある方は両方を合わせて読んでいただけると幸いです。 ここでは、その結論のみ述べます。コードを読み解くと Vertex AI Experiments は Vertex ML Metadata というメタデータ管理サービスの実験管理観点でのラッパーであること がわかりました。Vertex AI Experiments の Experiment オブジェクトや ExperimentRun オブジェクトの実態は、Vertex ML Metadata の Context というオブジェクトに紐づいていました。 このことをきっかけに Context オブジェクトを読み解くと、 Context オブジェクトは自らのメタデータを更新するメソッドが実装されており、こちらを利用することでテスト実行のメタデータを扱えました 。結果として課題だった、 set_tags() の置き換えを実現できました。 API コールの上限 上述の一部互換課題を解決したことで、MLflow の置き換え検証を進めることができました。置き換え最中は何度も実行と修正を繰り替えすので、実験管理機能を使用する操作の規模は最小限のものとしていました。 一通りコードの置き換えが完了したので、実験管理機能を使用する操作の規模を徐々に大きくしていたところ下記のエラーが発生しました。 ERROR 429 Quota exceeded for quota metric 'Resource management (CRUD) requests' and limit 'Resource management (CRUD) requests per minute per region' of service 'aiplatform.googleapis.com' for consumer 'project_number:***'. reason: "RATE_LIMIT_EXCEEDED". 少し解説すると、 aiplatform.googleapis.com とは Vertex AI 周りの API エンドポイントのドメインを指します。AI Platform は Vertex AI の前身のサービス名だったと思います。 Quota exceeded for quota metric 'Resource management (CRUD) requests' ということなので、 リソースに対する操作の指標が上限に達したと言われています。 Vertex AI に限らずですが、Google Cloud のリソースにはものによっては上限が割り当てられています。Vertex AI に関しては、 こちら のページから各種上限を確認できます。この中でも リソース管理(CRUD)リクエストの 1 分間あたりのリクエスト数が 600 というものが上述のエラーに該当します。 Google Cloud では、割り当てを使用して公平性を確保し、リソースの使用量と可用性の急増を抑えます。割り当ては、Google Cloud プロジェクトで使用できる Google Cloud リソースの量を制限します。割り当ては、ハードウェア、ソフトウェア、ネットワーク コンポーネントなど、さまざまなリソースタイプに適用されます。 引用: https://cloud.google.com/vertex-ai/docs/quotas?hl=ja 今回でいえば、 テスト実行を検索して取得する部分でこちらのエラーが発生していることがわかりました 。上述した通り学習を実行した際、ニアアリルタイムにテスト実行の結果を取得するために秒間 3~4 回ほどの取得リクエストを投げています。この操作時間が長ければそれだけリクエスト数が増加することとなります。 結果として、 この操作が Vertex AI Experiments におけるテスト実行情報の取得について想定を超えるような使い方となっており、API コールの上限に達したということ でした。 こちらの上限は申請すれば緩和させることができるのですが、今回のような検証レベルのユースケースでこの上限に達してエラーが出たということは、複数人が同時に該当の操作をするような実際のユースケース場合では上限を緩和させてもすぐに達してしてしまうことが考えられました。 考察と今後 上述した検証レベルのユースケースで API コールの上限に達したという点について、 現状の Node-AI の MLflow への API コールの仕方は想定外の使い方であることが考えられました 。だからこそ、MLflow がパフォーマンスがボトルネックになっているとこいうことにつながっているのかもしれません。 こちらの検討で現状の Node-AI の該当の実装があまりイケていないことが改めてわかりました。今後取り得る選択肢としては、 API コールを減らすような実装の見直ししたのちに MLflow を Vertex AI Experiments に置き換えるといったところでしょうか 。 バックエンドの API コールの減少は、フロントの振る舞いも加味して調整する必要があるので、慎重に進める必要がありそうです。 さいごに 今回 Node-AI で利用している MLflow のパフォーマンス面とセキュリティ面の課題に対して Google Cloud の Vertex AI Experiments というサービスの置き換えを検討しました。 検討の中で、テスト実行のメタデータの扱いと API コールの上限の 2 つの課題にぶつかり、前者は解決できたものの後者はクリティカルなものでした。この検討の考察から取り得る選択肢を洗い出すといったとこまでを本記事でまとめました。 かなり長くなってしまいました。ここまでお読みいただいた方、ありがとうございました! 明日以降のブログにもご期待ください!
アバター
この記事は、 NTT Communications Advent Calendar 2024 14日目の記事です。 脆弱性対応の分野で注目度が高まりつつあるSSVCの概要と、その運用方法について紹介します。 脆弱性対応の課題 公開される脆弱性の増加 実際に悪用される脆弱性は一部に過ぎない 脆弱性の悪用までが高速化 CVSSによる脆弱性のトリアージ SSVCとは SSVCを構成する要素 ステークホルダーのロールの特定 決定すべき優先度(Decision)の特定 Decisionが取りうる値(Outcome)の定義 Outcomeの算出に使う入力の決定(Decision Points) Outcomeの算出方法の決定(Policy) 要素のまとめ 既定のDecision Modelの活用 Deployer Decision Model SSVCの活用 組織に合わせたModelの選択 SSVC運用のデータの取得 まとめ こんにちは。イノベーションセンターの志村( @mshim03 )です。 普段はMetemcyber PJというチームでSBOMを利用した脆弱性対応の研究開発の取り組みや、Network Analytics for Security(通称NA4Sec) PJで攻撃インフラの解明・撲滅に関する技術開発、自組織のセキュリティ運用に取り組んでいます。 本記事では、脆弱性対応の文脈で注目度が高まっているSSVCとは何か、どのような問題を解決するのか、どのように利用すべきかについて紹介します。 本記事では、サーバやソフトウェアが依存するソフトウェア(パッケージ)で公開されている脆弱性情報を特定し、パッチ対応などを行うことを脆弱性対応と呼称します。 脆弱性対応の課題 サービスを開発、運用する上では、利用しているソフトウェアなどで発見された脆弱性に対応していくことが欠かせません。 公開された脆弱性に対してパッチ適用を実施したり、適切な設定変更や緩和策を適用するなど、悪用を防ぐための活動が必要になります。 しかし現在の脆弱性対応には、以下のような課題が存在しています。 公開される脆弱性の増加 CVE-ID (CVE:共通脆弱性識別子のID) が付与された脆弱性の数は年々増加傾向にあります。 2023年の公開数29,066件に対し、2024年は37,514件 (12/9現在) の脆弱性がすでに公開されており、急速に脆弱性公開数が増加していることがわかります。 CVE Details より 実際に悪用される脆弱性は一部に過ぎない 大量の脆弱性が公開される一方で、実際に悪用される脆弱性の数はわずかです。 悪用が確認された脆弱性は全体の6%ほどだったという調査結果が存在しています。 1 脆弱性の悪用までが高速化 実際に悪用される脆弱性は一部ですが、攻撃者にとって有用な場合は即座に攻撃が開始される傾向にあります。 例えば、2024年に発生したPalo Alto Networks製品の脆弱性は、4月12日に情報が公開され、4月14日時点ですでに国内での攻撃が観測されていました。 2 すなわち、昨今の脆弱性を取り巻く環境は以下のような状態といえます。 脆弱性の数は増加しており、大量の脆弱性情報が届けられる 一方で大半の脆弱性は悪用されない ただし攻撃者にとって有用な脆弱性な場合、即座に対応しないと被害を受ける蓋然性が高い そのためすべての脆弱性に対応するというよりも、対処が必要な脆弱性を素早く特定・対応することが必要になっています。 このような活動は脆弱性のトリアージと呼ばれることもあります。 CVSSによる脆弱性のトリアージ トリアージを実施する際の問題は「何を基準に優先順位を決定するのか」という点があります。 悪用された際の影響の大きさや、悪用がどの程度考えられるかといった観点で優先度を考える必要があります。 脆弱性の危険度を表す指標としてはCVSS(共通脆弱性評価システム)が有名です。 CVSSは脆弱性に関するオープンな評価指標であり、悪用時の影響や攻撃の前提条件などをもとに0~10.0 までの値で算出されます。 一方でCVSSは攻撃の技術的評価をスコアリングしたものであり、意思決定に利用するには以下のような問題があると指摘されています。 脆弱性対応でよく使われるCVSS基本スコアは脆弱性を技術的観点で評価したもので、悪用可能性などは考慮していない 具体的な意思決定までサポートがされない CVSSスコアがある値より上なら即座に修正すべき、といった基準を与えるものではない CVSSスコアは高いものの実際には悪用困難で攻撃が発生していない脆弱性が存在するなど、CVSSスコアのみをトリアージに使うのは現実的でないことが多いです。 そのような状況の中、脆弱性対応の意思決定をサポートする概念としてSSVCが注目されています。 SSVCとは SSVC (Stakeholder-Specific Vulnerability Categorization)は、カーネギーメロン大学ソフトウェア工学研究所によって提案された手法です。 脆弱性管理に関わるステークホルダー (脆弱性に対するパッチを作成する開発者、脆弱性に対するパッチを適用するソフトウェア利用者など)のニーズに基づいて脆弱性に優先順位をつけ、対応順を明確にできる方法論となっています。 SSVCは以下のドキュメントで詳細が紹介されています。 https://certcc.github.io/SSVC/ SSVCは、Decision Tree(決定木)を用いて、数値ではなく脆弱性対応の優先度を出力します。 そのため最終的に算出度される優先度が明確であり、なぜその優先度が算出されたかを即座に理解できます。 以下はSSVCが用意している、ソフトウェア利用者(Deployer)向けの決定木(一部抜粋)です。 四角で囲まれたDecision Points(後述)の値によって判定が分岐し、最終的に1つの優先度が出力されます。 以下の例ですと、 「Exploitation(脆弱性の悪用状況)」が「PoC(悪用コードのPoCが存在)」、「Exposure(システムのネットワーク接続状態)」が「controlled(制限されている)」、 「Automatable(脆弱性の悪用を自動化できるか)」が「no」、「Human Impact(脆弱性の影響)」が「low」 ならば、最終的に「defer(現時点では脆弱性に対応する必要はない) 」という結果が出力されます。 SSVCの決定木の一部: 3 SSVCを脆弱性管理に活用しようという動きは近年増しており、IPAが発行する 脆弱性対応における リスク評価手法のまとめ で言及されるなど、存在感を高めています。 SSVCを構成する要素 SSVCの方法論を構築する要素を、 SSVC Howto ページをベースに紹介します。SSVCを利用して優先度を算出するためには、決定しなければならない要素がいくつかあります。 ステークホルダーのロールの特定 SSVCは “Stakeholder Specific” という名が表すように、脆弱性管理におけるステークホルダー、すなわちどのように脆弱性対応に関わるか、という概念が重要になります。 SSVCのドキュメントでは、以下のようなステークホルダーの立場が想定されています。 Supplier ソフトウェア開発者。脆弱性が発見された時にパッチを作成する役割を担う Deployer ソフトウェア利用者。提供されたパッチを環境に適用する役割を担う Coordinator CERTなどの立場が想定されている。脆弱性対応を統制したり、脆弱性情報の展開などをする役割を担う。 決定すべき優先度(Decision)の特定 脆弱性対応への関わり方によって、決定すべき優先度は変わります。 例えばSupplierが算出すべき優先度は、「どの順序で脆弱性のパッチを作成・提供すべきか」になりますし、Deployerであれば「どの順序で脆弱性パッチを適用するか」になります。 このように、ステークホルダーが決定すべき優先度を、SSVCではDecisionと呼びます。 Decisionが取りうる値(Outcome)の定義 Decisionの取りうる値が定義されている必要があります。 SSVCではこれをOutcomeと呼びます。 SSVCが提供するDeplolyer向けのModelでは、以下のようなOutcomeが定義されています。 Defer 現時点で対応しない。 Scheduled 定期的なメンテナンスで対応する。 Out-of-cycle 定期的なメンテナンスは別に、早期に軽減策か修正策を適用する (次の機会か、必要ならば作業時間外での実施)。 Immediate 脆弱性に即時対応する。すべてのリソースをできるだけ早く脆弱性の修正に集中させる。必要であれば組織の通常の運用を停止させる。 優先度はImmediateが最も高く、Deferが最も低いです。 Outcomeの算出に使う入力の決定(Decision Points) どのような情報をもとにOutcomeを算出するかを決定します。 この入力をDecision Pointsと呼びます。 Decision Pointsの例としては、脆弱性の悪用状況、脆弱性の対象となるシステムのネットワーク接続状態などがあります。 SSVCではさまざまなDecision Pointsを定義しています。 以下ページより参照できます。 https://certcc.github.io/SSVC/reference/decision_points/ Outcomeの算出方法の決定(Policy) Decision PointsからOutcomeを算出する方法を決定します。 これをPolicyと呼びます。PolicyはDecision Pointsを入力として受け取り、Outcomeを返す関数と考えることができます。 SSVCはPolicyの表現として決定木を使うことが一般的です。 決定木を活用することで、各Decision PointsがどのようにOutcomeの算出に使われるのか明確になります。 要素のまとめ SSVCを構成する要素をおさらいします。 Stakeholder Decision Outcome Decision Points Policy SSVCを構成するこれらの要素をまとめた概念をDecision Modelと呼びます。 SSVCを用いた意思決定をする場合は、自組織のニーズにあった適切なDecision Modelを決定し、それを運用に落とし込む必要があります。 既定のDecision Modelの活用 SSVCを利用するために、Decision Modelを一から構築するのは難易度が高いです。 そのためSSVCは、あらかじめSupplier、Deployer、Coordinatorのステークホルダー向けのDecision Modelを提供しています。 それぞれ Supplier Decision Model 、 Deployer Decision Model 、 Coordinator Decision Models 4 と呼ばれています。 Deployer Decision Model 最も多くのユーザに関係するであろう、Deployer Decision Modelについて紹介します。 このModelでは、Stakeholderは「Deployer」、Decisionは「パッチを適用する優先度」になります。 Outcomeは前述したDefer、Scheduled、Out-of-cycle、Immediateです。 Policyは以下の決定木で表されます。 この決定木はDeployer Treeと呼称されます。 Deployer Tree: 5 Deployer Treeは以下のようなDecision Pointsを有しています。 脆弱性自体の情報と、脆弱性対応の対象となる資産の情報が主に含まれています。 Exploitation 現時点での脆弱性の悪用状況。時間と共に変化しうる PoCの有無や、実際の攻撃が悪用されているかによって判断される System Exposure システム・サービスのアタックサーフェース(攻撃可能な境界)のアクセス可能状態 インターネットなどからアクセス可能か、アクセスが制限されているかなどによって決定される Automatable 脆弱性を利用した攻撃が自動化可能か(人間のインタラクションがなくても実行可能か) Human Impact その脆弱性が及ぼす影響の大きさを表す。Safety Impact と Mission Impactから算出される 6 Safety Impact 脆弱性が安全性に与える影響 Mission Impact 組織のミッションに不可欠な機能へ与える影響 SSVCの活用 SSVCを実際に活用する際のポイントを2つ紹介します。 組織に合わせたModelの選択 Deployer Decision Modelをそのまま採用するには、資産管理を適切に実施し、資産の外部への露出状況や用途をデータとして継続的に収集する必要があります。 これは組織の規模や状態によっては難しいことがあります。 そのような場合、既存のModelをカスタマイズしたり、組織のポシリーに合致するようなDecisionやDecision Pointsを採用することも可能です。 7 SSVCのドキュメントでは、組織のリソースに応じて以下のように、段階的にDecision Modelを成長させていく例が示されています。 https://certcc.github.io/SSVC/howto/acuity_ramp/#an-acuity-ramp-in-action CISA KEV 8 を利用 Exploitationを利用 Exploitation, System Exposureを利用 Exploitation, System Exposure, Automatable を利用 Exploitation, System Exposure, Automatable, Mission Impact, Safety Impact を利用 SSVC運用のデータの取得 SSVCはDecision Points のためのデータをどのように収集するかは定義していません。 ( Reference ページに目安となる方法は紹介されています。) 組織はDecision Pointsの決定に必要なデータを収集し、適切にマッピングする方法を決定する必要があります。 Deployer TreeのDecision Modelを利用する場合、収集すべき情報は主に脆弱性自体の情報と、ソフトウェアをデプロイしている環境の情報になります。 脆弱性自体の情報としてExploitationやAutomatableなどの情報がありますが、これらはCISAが情報を提供しています。 この情報は Vulnrichment GitHub Repository や、CVEの CISA ADP から参照できます。 9 CISA ADPは、 CVE Website で提供される脆弱性ページから参照できます。 10 View JSON を選択すればJSON形式で取得可能で、プロセスの自動化に役立てられます。 11 環境情報の収集は資産管理と深く紐づいており、組織の資産管理のデータをDecision Pointsに反映する方法を定義する必要があります。 またShodanなどの公開デバイスの検索サービスや、ASM(Attack Surface Management)サービスを利用してのデータ収集も可能でしょう。 まとめ SSVCを活用することで、脆弱性対応の優先度を算出し、組織の対応順序を明確にできます。 また利用するデータとその影響が明確になり、改善しやすい脆弱性対応が可能になります。 SSVCを活用することで何を決定すべきか、そのためにどんな情報が必要かを明確にできます。 意思決定プロセスが明確になることで、改善も容易になっています。 NTTコミュニケーションズではSSVCとSBOMを組み合わせて脆弱性対応の意思決定をサポートするツールを開発しています。 SBOM、SSVCというワードに興味がある方はぜひチェックしてください。 https://github.com/nttcom/threatconnectome それでは、明日の記事もお楽しみに! https://jp.tenable.com/blog/epss-shows-strong-performance-in-predicting-exploits-says-study-from-cyentia-and-first ↩ https://www.jpcert.or.jp/pr/2024/PR_Report2024Q1.pdf ↩ https://github.com/CERTCC/SSVC/blob/679610194d4062d5638aeb6df6d0f561a0c415c9/docs/pdf/ssvc_2_deployer_SeEUMss.pdf ↩ 複数形になっているのは、Coordinator向けのModelは2つ存在しているからです。受け取った脆弱性レポートに基づいて調整を実施するかを決定する Triage に関するModelと、脆弱性を公開するかを決定する Publication に関するModelです。 ↩ https://github.com/CERTCC/SSVC/blob/679610194d4062d5638aeb6df6d0f561a0c415c9/docs/pdf/ssvc_2_deployer_SeEUMss.pdf ↩ Human Impactを脆弱性ごとに算出することが現実的かについては議論があります。 FutureVuls Blog などをご参照ください。 ↩ 独自のDecision PointsやDecisionを採用する例は、SSVCの Prepare to Use SSVC に記載されています。業界規制やSLAをDecision Pointsとして採用する事例が紹介されています。 ↩ CISA KEV は、CISAが公開している悪用が確認された脆弱性のカタログです。 ↩ Authorized Data Publisher(ADP) は、認定された組織がCVEレコードに追加情報を付与できる仕組みです。詳細はCVE Websiteの ADPページ を参照ください。 ↩ ブログ内で紹介したPalalto脆弱性(CVE-2024-3400)の場合、次のURLでCISA ADPを含んだ情報が公開されています: https://www.cve.org/CVERecord?id=CVE-2024-3400 ↩ CVE-2024-3400の場合、次のURLからJSON形式で取得できます: https://cveawg.mitre.org/api/cve/CVE-2024-3400 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 13 日目の記事です。 フロントエンドのインフラ構成を見直すことで運用コストを 99%削減できた事例についてご紹介します。 目次 目次 はじめに 見直しの背景 これまでの環境 課題 実現方法 工夫した点 動的コンテンツを表示させる 設定の自動化 変更後の効果 おわりに はじめに こんにちは、 NeWork 開発チームの栄です。 普段はオンラインワークスペースサービス NeWork の開発エンジニアをしています。 この記事では、これまで NeWork で利用していたフロントエンドのインフラ構成を見直した結果、運用コストを 99%削減できた事例をご紹介します。 見直しの背景 これまでの環境 NeWork のサービス基盤は Google Cloud 上で提供しており、フロントエンドは Google App Engine (以下、App Engine) を利用しています。 課題 この運用を続けてきてきましたが、App Engine の運用コストが高いという課題がありました。 利用者ごとに異なる動的なコンテンツを配信するために App Engine を利用していましたが、App Engine はインスタンスの起動時間に応じて課金がされるサービスです。 起動していない時間は料金が発生しませんが、NeWork では利用人数や利用頻度からパフォーマンス面を考慮して常時複数台のインスタンスを起動していました。 そのため、毎月かなりのコストがかかっており、NeWork が利用する Google Cloud の料金の多くを占めるのがこの App Engine の料金でした。 実現方法 上述の背景を考慮してインフラ構成見直しの検討を始めたところ、Firebase Hosting というサービスが候補にあがりました。 1 Firebase Hosting は Firebase 社が提供するサービスの 1 つで、ウェブアプリを公開できるウェブホスティングサービスです。 静的ウェブアプリや単一ページウェブアプリの配信に適しており、CDN(コンテンツ配信ネットワーク)にキャッシュが保存されることでコンテンツを高速に配信できます。 料金に関しては利用時間に応じた課金体系ではなく、下記項目の実際の使用量に応じて課金がされるのですが、あらかじめ無料枠が用意されており、無料枠のしきい値を超える使用量に対してのみ課金がされます。 コンテンツの保存に必要なストレージ容量 エンドユーザーに転送されたデータの量 無料枠で十分なストレージ容量があること、NeWork は 1 つの画面で大半の操作を完結できるのであまり多くのデータ量が転送されないと想定されたため、料金を大きく削減できると見込んで Firebase Hosting への移行を決定しました。 工夫した点 動的コンテンツを表示させる 通常 Next.js では動的コンテンツを配信するために Node.js サーバーが必要になるのですが、Firebase Hosting へ移行する場合 Node.js サーバーを利用できないため、すべて静的コンテンツ(HTML/CSS/JavaScript)に変換してから利用する必要があります。 そのため、Next.js の Static Exports というウェブアプリを静的コンテンツとしてエクスポートできる機能を利用して Firebase Hosting に設定をしてみました。 しかし、その場合は動的コンテンツを配信できなかったため、契約者ごとに表示内容が異なるページをうまく表示ができませんでした。 例:本来は契約者ごとに利用できるルームの数が違うはずなのに、全ての契約者のルーム数が同じになってしまう。 もう少し詳細に調べたところ、Firebase Hosting にはあらかじめ正規表現を定義しておき、パターンに一致する URL に対して適切なページを表示できるリライト機能がありました。 この機能を利用することで、動的コンテンツの配信に対応ができました。 設定の自動化 しかし、新たなページが増えるごとに URL パターンを手動で追加して管理するのは保守性に優れません。 何か手立てがないか調べたところ、Next.js がビルド時に生成する routes-manifest.json というファイルに動的コンテンツのページ URL と正規表現のセットで出力されることがわかりました。 // routes-manifest.jsonファイル ※一部のみ記載 { " version ": 3 , " pages404 ": true , " basePath ": "", " dynamicRoutes ": [ { " page ": " /contractors/[[...contractorId]] ", // ページURL " regex ": " ^/contractors(?:/(.+?))?(?:/)?$ ", // ページURLの正規表現 " routeKeys ": { " contractorId ": " contractorId " } , " namedRegex ": " ^/contractors(?:/(?<contractorId>.+?))?(?:/)?$ " } // 以下省略 ] // 以下省略 } そこでスクリプトを作成して自動で管理できるようにすることで、設定の抜け漏れや間違いが発生しないようにしました。 // わかりやすさを考慮して、エラー処理等は省略しています。 const fs = require ( "fs" ) ; const path = require ( "path" ) ; // JSONファイルを読み込む関数 const readJsonFile = ( filePath ) => { const content = fs . readFileSync ( filePath , "utf-8" ) ; return JSON . parse ( content ) ; } ; // Next.js の Dynamic Routes を Firebase Hosting の rewrites で利用できる形に変換する関数 const generateRewrites = ( dynamicRoutes ) => { return dynamicRoutes . map (({ regex , page }) => ({ regex , destination : ` ${ page } .html` , })) ; } ; const main = () => { // 1. routes-manifest.jsonファイルを読み込む const routesManifestPath = process . argv [ 2 ] ; // 2. Firebase Hosting で利用する rewrites ルールを生成 const { dynamicRoutes } = readJsonFile ( routesManifestPath ) ; const rewrites = generateRewrites ( dynamicRoutes ) ; // 3. あらかじめ作成したFirebase Hosting の設定ファイルテンプレートに rewrites ルールを結合 const firebaseTemplatePath = path . join ( __dirname , "firebase.template.json" ) ; const firebaseConfig = readJsonFile ( firebaseTemplatePath ) ; firebaseConfig . hosting . rewrites = rewrites ; // 4. Firebase Hosting の設定ファイルを出力 const firebaseConfigPath = path . join ( __dirname , "firebase.json" ) ; fs . writeFileSync ( firebaseConfigPath , JSON . stringify ( firebaseConfig , null , 2 )) ; } ; main () ; 最終的には下記のような設定ファイルが作成されることになります。 // firebase.json ※一部のみ記載 { " hosting ": { " target ": " web ", " public ": " public ", " cleanUrls ": false , " trailingSlash ": false , " ignore ": [ " firebase.json ", " **/.* ", " **/node_modules/** " ] , " headers ": [ // 以下省略 ] , " rewrites ": [ { " regex ": " ^/contractors(?:/(.+?))?(?:/)?$ ", " destination ": " /contractors/[[...contractorId]].html " } // 以下省略 ] } } 変更後の効果 課題にあった「 App Engine のランニングコストが高い」ですが、劇的に改善される結果となりました。 見直し前の料金: 月額およそ 380,000 円 見直し後の料金: 月額およそ 1,400 円 そのため、年額換算するとおよそ 4,400,000 円のコストを削減しており、インフラ構成見直し前と比較すると 99%以上のコストを削減できる結果となりました。 おわりに この記事では、インフラ構成を見直すことで運用コストを大幅に削減できた事例についてご紹介しました。 今回はサービス全体の中のフロントエンド部分のみの変更となりましたが、他にも改善できるところはあると感じているので、継続してコスト改善を検討できればと考えています。 サービスに使用している技術の制約などで必ずしもこのような取り組みができるとは限りませんが、運用コストを大きく削減できた一例として皆さまのご参考になれば幸いです。 現在 NeWork はどなたでも無料でお試しできますので、もしプロダクトや使われている技術に興味を持っていただけたらぜひ触ってみてください。 明日のアドベントカレンダーもお楽しみに。 Firebase Hosting 公式ドキュメント ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024  12日目の記事です。 Azure Databricksを使ってレイクハウスアーキテクチャのログ基盤を構築し、 構造化されていないアプリケーションログの保管や加工、分析を試します。 はじめに レイクハウスアーキテクチャ ログ基盤とレイクハウス Azure Databricksでアプリケーションログを分析する Azure Databricksの準備 Terraformを使ったリソース作成 カタログとスキーマの作成 ログの取り込み ログの加工 BronzeからSilver SliverからGold ログの分析 (可視化) まとめ 参考文献 はじめに こんにちは、コミュニケーション&アプリケーションサービス部の吉仲です。 2022年度に入社し、初期配属からメール系システムと文書要約APIの開発・運用業務に取り組んでいます。 今回は業務から少し離れ、自身が興味のあるデータエンジニアリングの分野を題材にします。 本記事では、Azure Databricksを使ってレイクハウスアーキテクチャのログ基盤を構築し、 そこへ非構造化ログを取り込み、データの加工やダッシュボードでの可視化を試していきます。 本記事に含まれる内容は以下の通りです。 なぜログ基盤をレイクハウスアーキテクチャにしたいか Azure Databricksの構築 (Terraformを使用) Delta Live Tablesを使ったパイプラインの実装 サンプルログを地図上にマッピングして可視化するダッシュボードの作成 レイクハウスアーキテクチャ レイクハウスとは、 Databricks が提唱している新しいタイプのデータマネジメントアーキテクチャです。 www.databricks.com 簡単に言うと、レイクハウスはデータ レイク とデータウェア ハウス の良いとこ取りをしたアーキテクチャです。 レイクハウスは、構造化データから非構造化データまでを一元的かつ効率的に扱うことができ、 ビジネスインテリジェンス (BI) とAI/機械学習のどちらにも対応します。 ログ基盤とレイクハウス ログ基盤を構築する目的として、ログの 保管 と 分析 の2つがあると思います。 この保管と分析を 低コスト かつ 効率的 に実現できる点に、ログ基盤でレイクハウスアーキテクチャを取るメリットがあると考えます。 各種のアプリケーションやシステムにおいて、全てが構造化ロギングを行っているとは限りません。 例えば、OSSのメール転送エージェント (MTA) であるpostfixでは、メール配送時に以下のようなログを出力します。 見ての通りjsonやxml形式で出力されておらず、なおかつ1回のメール配送の情報が複数行に点在しています。 Dec 12 08:13:10 mtaserver postfix/smtpd[1180]: connect from example.com[192.0.2.1] Dec 12 08:13:10 mtaserver postfix/smtpd[1180]: ABCDEF01: client=example.com[192.0.2.1] Dec 12 08:13:10 mtaserver postfix/cleanup[1187]: ABCDEF01: message-id=<message.id@mtaserver.example.com> Dec 12 08:13:11 mtaserver postfix/smtpd[1180]: disconnect from example.com[192.0.2.1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5 Dec 12 08:13:11 mtaserver postfix/qmgr[264]: ABCDEF01: from=<from@example.com>, size=2200, nrcpt=1 (queue active) Dec 12 08:13:11 mtaserver postfix/smtp[1738]: ABCDEF01: to=<to@example.net>, relay=example.net[192.0.2.2]:25, delay=0.3, delays=0.2/0/0.07/0.03, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as ABCDEF01) Dec 12 08:13:11 mtaserver postfix/qmgr[264]: ABCDEF01: removed このようなログは未加工のままでは扱いが難しいため、加工して構造化したログを用意した上で分析します。 ただし、加工によって少なからず情報が抜け落ちてしまうことがあります。 将来的に、加工方法や分析目的が変化/追加されることを考慮すると、未加工のログ (生ログ) も保管しておきたいです。 データレイクとデータウェアハウスを組み合わすことで、生ログの保管と加工済みログの分析を効果的に実現できます。 しかし、この構成ではデータを二重で持つことになり、データ保存コストが高くなりやすいです。 ここで、レイクハウスの出番です。 データレイク : さまざまなデータを低コストで保管できる データウェアハウス : 高度な分析を効率的に行える この両方の強みを兼ね備えたレイクハウスにより、生ログの保管と加工済みログの分析を 低コスト かつ 効率的 に実現できると考えます。 また、後述するマルチレイヤーのデータ設計により、加工方法や分析目的の変化/追加に対して柔軟で拡張性の高い基盤を実現できます。 Azure Databricksでアプリケーションログを分析する ここでは、 Azure Databricks を使ってログ基盤としてのレイクハウスを構築し、サンプルのログを分析してみます。 具体的には、前述のpostfixサンプルログを保管し、分析するところまでをAzure Databricksで試します。 なお、ログ基盤を構築・運用する上ではログの 収集 も非常に重要なポイントですが、今回はフォーカスしません。 簡単のため、データはAzure Databricksに直接アップロードすることとします。 Azure Databricksの準備 まずはじめに、以下のような構成でAzure Databricksを構築します。 リソースグループの中に以下のリソースを作成します。 仮想ネットワーク ネットワークセキュリティグループ Azure Databricksワークスペース ワークスペースを作成すると、自動的に専用のリソースグループと以下のリソースが作成されます。 ストレージアカウント (Azure Data Lake Storage Gen2, ADLS2) Azure Databricks用のアクセス コネクタ マネージドID Databricks上のデータマネジメント機能 Unity Catalog の外部ロケーションとしてADLS2を登録している状態が出来上がります。 1 Terraformを使ったリソース作成 今回は Terraform を使ってリソースを作成していきます。 前提は以下の通りです。 Azureで「共同作成者」ロールが割り当てられたアカウントを保持していること Azure CLI およびTerraformを実行できる環境があること Azure CLIで上記のアカウントにログイン済みであること こちら を参考に、 Azureのリソースグループと仮想ネットワーク、Azure Databricksワークスペースを定義します。 今回はリソースの名前に "databricksdemo" というprefixを付けます。 Terraformコード terraform { required_providers { azurerm = "~> 4.0" random = "~> 3.6" databricks = { source = "databricks/databricks" version = "1.59.0" } } } data "azurerm_client_config" "current" {} data "external" "me" { program = [ "az" , "account" , "show" , "--query" , "user" ] } locals { subscription_id = "<AzureサブスクリプションID>" prefix = "databricksdemo" tags = { "Environment" = "Demo" "Owner" = lookup (data.external.me.result, "name" ) } region = "japaneast" cidr = "10.179.0.0/20" } provider "azurerm" { subscription_id = local.subscription_id features {} } provider "databricks" { host = azurerm_databricks_workspace.this.workspace_url } resource "azurerm_resource_group" "this" { name = "$ { local.prefix } -rg" location = local.region tags = local.tags } resource "azurerm_virtual_network" "this" { name = "$ { local.prefix } -vnet" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location address_space = [ local.cidr ] tags = local.tags } resource "azurerm_network_security_group" "this" { name = "$ { local.prefix } -nsg" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location tags = local.tags } resource "azurerm_subnet" "public" { name = "$ { local.prefix } -public" resource_group_name = azurerm_resource_group.this.name virtual_network_name = azurerm_virtual_network.this.name address_prefixes = [ cidrsubnet (local.cidr, 3 , 0 ) ] delegation { name = "databricks" service_delegation { name = "Microsoft.Databricks/workspaces" actions = [ "Microsoft.Network/virtualNetworks/subnets/join/action" , "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action" , "Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action" , ] } } } resource "azurerm_subnet_network_security_group_association" "public" { subnet_id = azurerm_subnet.public.id network_security_group_id = azurerm_network_security_group.this.id } resource "azurerm_subnet" "private" { name = "$ { local.prefix } -private" resource_group_name = azurerm_resource_group.this.name virtual_network_name = azurerm_virtual_network.this.name address_prefixes = [ cidrsubnet (local.cidr, 3 , 1 ) ] delegation { name = "databricks" service_delegation { name = "Microsoft.Databricks/workspaces" actions = [ "Microsoft.Network/virtualNetworks/subnets/join/action" , "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action" , "Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action" , ] } } } resource "azurerm_subnet_network_security_group_association" "private" { subnet_id = azurerm_subnet.private.id network_security_group_id = azurerm_network_security_group.this.id } resource "azurerm_databricks_workspace" "this" { name = "$ { local.prefix } -workspace" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location sku = "premium" managed_resource_group_name = "$ { local.prefix } -workspace-rg" tags = local.tags custom_parameters { no_public_ip = true virtual_network_id = azurerm_virtual_network.this.id private_subnet_name = azurerm_subnet.private.name public_subnet_name = azurerm_subnet.public.name public_subnet_network_security_group_association_id = azurerm_subnet_network_security_group_association.public.id private_subnet_network_security_group_association_id = azurerm_subnet_network_security_group_association.private.id } } data "databricks_node_type" "smallest" { local_disk = true depends_on = [ azurerm_databricks_workspace.this ] } data "databricks_spark_version" "latest_lts" { long_term_support = true depends_on = [ azurerm_databricks_workspace.this ] } resource "databricks_cluster" "this" { cluster_name = "$ { local.prefix } -cluster" node_type_id = data.databricks_node_type.smallest.id spark_version = data.databricks_spark_version.latest_lts.id data_security_mode = "SINGLE_USER" autotermination_minutes = 10 spark_conf = { "spark.databricks.cluster.profile" : "singleNode" , "spark.master" : "local[*]" , "spark.databricks.unityCatalog.volumes.enabled" : "true" , } custom_tags = { "ResourceClass" : "SingleNode" } } output "databricks_host" { value = "https://$ { azurerm_databricks_workspace.this.workspace_url } /" } Terraform コマンドにより、上記で定義したリソースを作成します。 terraform init terraform plan terraform apply # 途中で "yes" を入力 # ... # Outputs: # databricks_host = "https://<AzureDatabricksワークスペースのURL>/" リソースグループ <prefix>-rg (今回は databricksdemo-rg ) の中に以下のリソースが作成されます。 <prefix>-nsg <prefix>-vnet <prefix>-workspace また、リソースグループ <prefix>-workspace-rg が自動的に作成され、その中に以下のリソースが作成されます。 dbmanagedidentity dbstorage<randam-string> unity-catalog-access-connector 以上で準備は完了です。 terraform apply 実行後に表示されるDatabricksワークスペースのURLをクリックし、Databricks UIを開きます。 カタログとスキーマの作成 Databricksでは、 メダリオンアーキテクチャ というマルチレイヤーのデータ設計が推奨されています。 具体的な説明はここでは省略しますが、このアーキテクチャにより、加工方法や分析目的の変化/追加に対して柔軟になります。 メダリオンアーキテクチャの実装方法については こちら を参考にし、以下のような設計とします。 demo というカタログを作成し、その中にBronze/Sliver/Goldレイヤー用のスキーマを作成します。 bronze :postfixの生ログをそのまま保存 silver :各行から必要な情報 (client IP等) を抽出し、構造化したログを保存 gold :複数行に跨るメール配送の情報を結合し、分析用に整備したログを保存 上記のカタログとスキーマは、以下の操作により作成します。 サイドバーで [カタログ] をクリック カタログ一覧の右上の [⚙]>[外部ロケーション] をクリックし、外部ロケーションのURLを控える サイドバー上部の [新規]>[ノートブック] から新しいノートブックを作成して以下を実行 (適宜、クラスターの起動とノートブックのアタッチを実施) %sql CREATE CATALOG demo MANAGED LOCATION ' <外部ボリュームのURL>/demo ' ; CREATE SCHEMA demo.bronze MANAGED LOCATION ' <外部ボリュームのURL>/demo/bronze ' ; CREATE SCHEMA demo.silver MANAGED LOCATION ' <外部ボリュームのURL>/demo/silver ' ; CREATE SCHEMA demo.gold MANAGED LOCATION ' <外部ボリュームのURL>/demo/gold ' ; 【参考】スキーマ作成後の状態 以上でカタログとスキーマの作成は完了です。 ログの取り込み 次に、Databricks UI上で前述のpostfixサンプルログをBronzeレイヤーに取り込みます。 なお、本格的なログ基盤を構築する場合は、Azureの Event Hubs や Data Factory 等を使ってデータソースからログを取り込みます。 以下の操作でボリュームとディレクトリを作成し、サンプルログをアップロードします。 先ほどのノートブックを開き、以下のクエリを実行 %sql CREATE VOLUME demo.bronze.maillog; サイドバーの [新規]>[データを追加またはアップロード] をクリック サンプルログを maillog というファイルとしてアップロードし、 送信先パスに /Volumes/demo/bronze/maillog/postfix/2024-12-12 を入力して [アップロード] をクリック 同様に、日時等を変えたサンプルログ②を /Volumes/demo/bronze/maillog/postfix/2024-12-13 にアップロードしておきます。 サンプルログ② Dec 13 18:15:30 mtaserver postfix/smtpd[1181]: connect from example.com[192.0.2.3] Dec 13 18:15:30 mtaserver postfix/smtpd[1181]: ABCDEF02: client=example.com[192.0.2.3] Dec 13 18:15:30 mtaserver postfix/cleanup[1188]: ABCDEF02: message-id=<message.id@mtaserver.example.com> Dec 13 18:15:31 mtaserver postfix/smtpd[1181]: disconnect from example.com[192.0.2.3] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5 Dec 13 18:15:31 mtaserver postfix/qmgr[265]: ABCDEF02: from=<from@example.com>, size=2200, nrcpt=1 (queue active) Dec 13 18:15:31 mtaserver postfix/smtp[1739]: ABCDEF02: to=<to@example.net>, relay=example.net[192.0.2.2]:25, delay=0.5, delays=0.4/0/0.07/0.03, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as ABCDEF02) Dec 13 18:15:31 mtaserver postfix/qmgr[265]: ABCDEF02: removed アップロードしたサンプルログをDatabricksで確認してみます。 先ほどのノートブックで以下を実行します。 %sql SELECT * from read_files( " /Volumes/demo/bronze/maillog/postfix/2024-12-*/maillog " , format => " text " ) Databricks上で、保管したサンプルログの中身を表示できるようになりました。 ただし、このままではログの検索や分析が難しいため、データ加工により構造化ログを作成していきます。 ログの加工 ここでは、 Delta Live Tables (DLT) パイプラインを使ってログを加工することで、 BronzeからSilver、そしてGoldレイヤーにデータをインジェストしていきます。 BronzeからSilver Bronzeレイヤーに保存した生ログを構造化してSilverレイヤーにインジェストします。 postfixログの構造化のために、 Logstash で使われるGrokという正規表現と、PythonでGrokを扱うための pygrok を利用します。 postfixログ用のGrokパターンは以下のものを使います。 これはあくまでサンプルログ用のパターンで、実際のログを構造化するためにはより複雑なパターンが必要になります。 postfixサンプルログ用のGrokパターン # common patterns PROCESS ([\w._\/%\-]+) PROGRAM (postfix[\w\-.]*) PROCESS_AND_PID %{PROGRAM:program}\/%{PROCESS:process}(?:\[%{NUMBER:pid}\])? QUEUEID (?:[0-9A-F]{6,}|[0-9a-zA-Z]{12,}|NOQUEUE) EMAIL_ADDRESSPART [a-zA-Z0-9_.+-=:~]+ EMAIL_ADDRESS %{EMAIL_ADDRESSPART}@%{EMAIL_ADDRESSPART} RELAY (?:%{HOSTNAME:relayhost}(?:\[%{IP:relayip}\](?::[0-9]+(.[0-9]+)?)?)?) CLIENT (?:%{HOSTNAME:clienthost}(?:\[%{IP:clientip}\](?::[0-9]+(.[0-9]+)?)?)?) POSREAL [0-9]+(.[0-9]+)? STATUS sent|deferred|bounced|expired|delivery RESPONSECODE [0-9][0-9][0-9] DSN %{NONNEGINT}.%{NONNEGINT}.%{NONNEGINT} # postfix/smtp POSTFIX_SMTP %{QUEUEID:qid}: to=<%{EMAIL_ADDRESS:to_addr}>, relay=<?%{RELAY}>?, delay=%{POSREAL:delay}, delays=%{DATA:delays}, dsn=%{DSN:dsn}, status=%{STATUS:result} \(%{DATA:reason}\) # postfix/smtpd POSTFIX_SMTPD %{POSTFIX_SMTPD_CLIENT}|%{POSTFIX_SMTPD_CONNECTS} POSTFIX_SMTPD_CLIENT %{QUEUEID:qid}: client=<?%{CLIENT}>? POSTFIX_SMTPD_CONNECTS (?:dis)?connect from %{CLIENT}(?: %{DATA:command_counts})? # postfix/cleanup POSTFIX_CLEANUP %{QUEUEID:qid}: (resent-)?message-id=<?%{DATA:messageid}>? # postfix/qmgr POSTFIX_QMGR %{QUEUEID:qid}: (?:removed|from=<(?:%{EMAIL_ADDRESS:from_addr})?>(?:, size=%{NUMBER:size}, nrcpt=%{NUMBER:nrcpt} \(%{GREEDYDATA:queuestatus}\))?) # aggregate all patterns POSTFIX_PREFIX (?:%{SYSLOGTIMESTAMP:timestamp}|%{TIMESTAMP_ISO8601:timestamp}) (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{PROCESS_AND_PID}: POSTFIX_LOG %{POSTFIX_PREFIX} (?:%{POSTFIX_SMTP}|%{POSTFIX_SMTPD}|%{POSTFIX_QMGR}|%{POSTFIX_CLEANUP}) このファイルを前述の手順で demo.bronze の管理ボリュームの other/grok/postfix.grok にアップロードしておきます。 続いて、DLTパイプラインを作成します。 サイドバーから [Delta Live Tables] を開き、画面右上の [パイプラインを作成] をクリックします。 以下の内容で設定後、[作成]をクリックします。(他はデフォルトでOK) パイプライン名: databricksdemo-bronze-to-silver 製品エディション: Core パス: (空のまま) ストレージオプション: Unity Catalog カタログ: demo ターゲットスキーマ: silver クラスターモード: 固定サイズ ワーカー: 1 ワーカータイプ: Standard_D3_v2 作成完了後、パイプライン詳細画面の右サイドバーからソースコード (ノートブック) のURLを開きます。 開いたノートブックに以下のソースコードを記載します。 # 依存関係インストール %pip install pygrok== 1.0 . 0 regex== 2024.11 . 6 # Grokパターンの定義 import json from pygrok import Grok grok_path = "/Volumes/demo/bronze/other/grok/" grok_pattern = "^%{POSTFIX_LOG}$" grok = Grok(grok_pattern, custom_patterns_dir=grok_path) # スキーマ、データ変換処理の定義 from pyspark.sql.functions import ( col, concat, current_date, from_json, lit, month, pandas_udf, regexp_extract, to_timestamp, year, ) from pyspark.sql.types import StringType, StructField, StructType keys = [ "timestamp" , "logsource" , "program" , "process" , "pid" , "qid" , "clienthost" , "clientip" , "messageid" , "command_counts" , "from_addr" , "size" , "nrcpt" , "queuestatus" , "to_addr" , "relayhost" , "relayip" , "delay" , "delays" , "dsn" , "result" , "reason" , ] schema = StructType([StructField(k, StringType(), True ) for k in keys]) @ pandas_udf ( "string" ) def parse_grok_udf (log_line): return log_line.apply( lambda x: json.dumps({k: v for k, v in grok.match(x).items() if v is not None }) ) # Bronzeレイヤーからのデータ読込み (一時ビュー作成) from datetime import datetime import dlt volume_path = f "/Volumes/demo/bronze/maillog/postfix/{datetime.now():%Y-%m}-*/maillog" # デモのため月次バッチ @ dlt.view () def raw_data (): return spark.read.format( "text" ).load(volume_path) # Silverレイヤーのテーブル作成 this_year = year(current_date()) dt_format = "yyyy MMM dd HH:mm:ss" @ dlt.table ( name= "maillog_postfix" , table_properties={ "quality" : "silver" }, ) def structured_data (): return ( dlt.read( "raw_data" ) .withColumn( "parsed" , from_json(parse_grok_udf(col( "value" )), schema)) .select( "parsed.*" ) .withColumn( "timestamp" , to_timestamp(concat(lit(this_year), lit( " " ), col( "timestamp" )), dt_format)) .sort(col( "timestamp" ).asc()) ) DLTパイプラインのクラスター databricksdemo-bronze-to-silver に接続後、[検証] をクリックします。 検証が完了して画面下部にグラフが描画された後、[起動] をクリックしてパイプラインを実行します。 すると、以下のように demo.silver.maillog_postfix というテーブルが作成されます。 作成されたテーブルは以下の通りです。 今回はGrokパターンを作り込んでいないので、完全には構造化できていない部分もあります ( command_counts や reason など)。 この部分も含めた構造化の難易度はそれほど高くないように思いますが、今回はここまでにします。 SliverからGold Goldレイヤーには、分析の目的に合わせたテーブルを作成します。 ここでは、メール送信もしくは受信の接続元IPを地図上にマッピングし、 異常な接続元の有無を可視化するような分析を実施していきます。 Goldレイヤーにテーブルを作成するために、以下の内容を実施します。 GeoLite2 から以下のCSVファイルをダウンロードし 2 、Databricksに取り込む GeoLite2-City-Blocks-IPv4.csv GeoLite2-City-Locations-en.csv 複数行に跨るメール配送の情報を結合 (1キューID/1レコードの状態にする) GeoLite2を使って、各メール配送ログのクライアントIPの地理情報を取得 1.について、postfixサンプルログやGrokパターンファイルと同様の手順で、 demo.bronze.other.geolite2 にCSVファイルをアップロードします。 その後、以下のコードを実行して demo.silver にテーブルを作成します。 ( spark.read.format( "csv" ) .option( "Header" , True ) .option( "inferSchema" , True ) .load( "/Volumes/demo/bronze/other/geolite2/<csv_file>" ) # CSVファイルを指定 .write.format( "delta" ) .mode( "overwrite" ) .saveAsTable( "demo.silver.<table_name>" ) # テーブル名を指定: `geolite2_city_blocks_ipv4` or `geolite2_city_locations_en` ) 2.について、前述の手順と同様にDLTパイプライン databricksdemo-silver-to-gold を作成し、 以下のソースコードを記載した上でパイプラインを実行します。 なお、地理情報のマッピング方法は こちら を参考にさせていただきました。 # IPアドレスと地理情報のマッピングのための変換処理の定義 import ipaddress as ip from pyspark.sql.functions import pandas_udf, col @ pandas_udf ( 'long' ) def to_network_address (cidr): return cidr.apply( lambda x: int (ip.IPv4Network(x).network_address)) @ pandas_udf ( 'long' ) def to_broadcast_address (cidr): return cidr.apply( lambda x: int (ip.IPv4Network(x).broadcast_address)) @ pandas_udf ( 'long' ) def to_address_int (cidr): return cidr.apply( lambda x: int (ip.IPv4Address(x))) # GeoLite2のテーブルの読み込み df_ip_blocks = spark.table( "demo.silver.geolite2_city_blocks_ipv4" ) df_locations = spark.table( "demo.silver.geolite2_city_locations_en" ) # 1キューID/1レコードに結合したビューの作成 import dlt from pyspark.sql.functions import collect_list, first from pyspark.sql.types import DateType @ dlt.view () def concatenated_data (): return ( spark.table( "demo.silver.maillog_postfix" ) .alias( "df" ) .groupBy(col( "timestamp" ).cast(DateType()).alias( "date" ), "logsource" , "qid" ) .agg( first( "timestamp" ).alias( "timestamp" ), first( "program" ).alias( "program" ), collect_list( "process" ).alias( "processes" ), first( "clienthost" , ignorenulls= True ).alias( "clienthost" ), first( "clientip" , ignorenulls= True ).alias( "clientip" ), first( "messageid" , ignorenulls= True ).alias( "messageid" ), first( "from_addr" , ignorenulls= True ).alias( "from_addr" ), first( "to_addr" , ignorenulls= True ).alias( "to_addr" ), first( "result" , ignorenulls= True ).alias( "result" ), first( "reason" , ignorenulls= True ).alias( "reason" ), ) .filter(col( "qid" ).isNotNull()) ) # Goldレイヤーのテーブル作成 @ dlt.table ( name= "maillog_postfix_with_locations" , table_properties={ "quality" : "gold" }, ) def maillog_postfix_with_locations (): return ( dlt.read( "concatenated_data" ) .hint( "range_json" , 65536 ) .join( df_ip_blocks.alias( "b" ), [ to_address_int(col( "clientip" )) > to_network_address(col( "b.network" )), to_address_int(col( "clientip" )) < to_broadcast_address(col( "b.network" )), ], "left" , ) .alias( "f" ) .join( df_locations.withColumnRenamed( "geoname_id" , "geoname_id_2" ).alias( "l" ), col( "f.geoname_id" ) == col( "l.geoname_id_2" ), "left" , ) ) パイプライン実行後は、以下のような demo.gold.maillog_postfix_with_locations テーブルが作成されます。 ログの分析 (可視化) 最後に、Goldレイヤーに作成したテーブルを使って、地図上に接続元をマッピングするダッシュボードを作成します。 なお、上で記載したpostfixサンプルログは例示用IPアドレスを使っているため、地理情報を取得できません。 以降の例では、適当なIPアドレスを使ったサンプルログでダッシュボードを作成しています。 ダッシュボード作成の手順は以下の通りです。 カタログから demo.gold.maillog_postfix_with_locations テーブルを開き、画面右上の [作成]>[ダッシュボード] をクリック 作成画面で、下部のナビゲーションから [ビジュアライゼーションを追加] をクリックしてボックスを配置 (デフォルトで配置されているビジュアライゼーションは削除してOK) 右側のメニューで、ビジュアライゼーションのパラメータを以下の通りに設定 可視化: ポイントマップ 緯度: latitude 経度: longitude 画面右上の [公開] をクリックし、アクセス許可のある人を確認した上で再度 [公開] をクリック 作成したダッシュボードは以下の通りです。 これにより、例えば社内メールが海外から送信されているといった事象を可視化できるようになりました。 本記事でのログ分析はここまでとします。 もちろん、Goldレイヤーのテーブルを使ってより高度な分析 (異常検知のモデル作成など) も可能だと思います。 まとめ 本記事では、Azure Databricksを使って非構造化ログ (postfixのサンプルログ) の保管から加工、可視化までを試しました。 Databricksでは、ログが構造化されているかを問わず、一元的なログ管理・分析が可能です。 同じUI上でパイプライン実装からデータ探索、ダッシュボード作成まで可能であり、非常に便利なプラットフォームと感じました。 また、今回は紹介しませんでしたが、Databricksにはマネージドの MLflow が組み込まれています。 レイクハウスはBI/AIの両方と親和性が高いことも強みなので、次はDatabricksを使ったMLOpsにも挑戦してみたいです。 最後までご覧いただきありがとうございました!それでは、明日の記事もお楽しみに! 参考文献 https://www.databricks.com/ https://github.com/databricks/terraform-provider-databricks/ https://registry.terraform.io/providers/databricks/databricks/latest/docs https://learn.microsoft.com/ja-jp/azure/databricks/ データブリックス・ジャパン (2022)『データブリックスクイックスタートガイド』 プロジェクトによっては、自動作成されるもの以外のストレージを外部ロケーションとしてUnity Catalogに登録する方法が適している場合もあります。 ↩ MaxMind のウェブサイトでユーザー登録することで、GeoLite2のデータをダウンロードできるようになります。 ↩
アバター
はじめに 開発部署へのOJTとしての挑戦 実際の開発業務でぶつかった壁 開発規模が大きい レビュアーにとって分かりやすいコードを書けない WebUIの開発において自分の想定と違う挙動になることがある 勉強方法の確立の難しさ 学習方法を確立する必要性 学習において大事だと感じたこと IT技術を体系的に(教科書的に)学ぶことは古い 効率的な学習方法 学習の高速道路 際立った個性 高速道路の高速化 超一流・一流になるために目指すべき人物像 NTTコミュニケーションズが与える学習の機会 おわりに 補足資料:ReactとRedux Reactとは JavaScriptのみの場合 Reactを導入した場合 Reactを使ってみた個人的な感想 Reduxとは Reactのみを使った場合 Reduxを導入した場合 Reduxを使ってみた個人的な感想 参考文献 はじめに こんにちは。情報セキュリティ部SMO2G・兼務イノベーションセンターテクノロジー部門MetemcyberPJの千坂知也と申します。今年新卒入社した一年目です。よろしくお願いいたします。 MetemcyberPJではOSSコントリビューターとして開発の業務に参加し、特にWebUI側の開発業務に多く携わってきました。 もともと大学院では、Pythonを使ってシミュレーション(厳密にいうとアルゴリズムの評価関数)のコードを書いていたもののWebUI系の開発言語は書いたことがなかったため、OJT先で一から学んだことになります。 言うまでもなく超一流のエンジニアではありませんが、OJTで気づいた(あるいは自分なりに感じた)学びや参考になった学習理論について特に学生、新入社員向けにまとめてみようかと思います。もし何か感じ入るものがあれば幸いです。 開発部署へのOJTとしての挑戦 初期配属として情報セキュリティ部に所属し、7月まで新入社員として研修を受けておりました。インシデントハンドリングや脆弱性に関する体系的な知識を会得する研修が多いなか、Webアプリ開発という研修があり、もともとモノ作りが好きだったことも相まって開発業務に興味を持ったのが始まりです。 情報セキュリティ部での一通りの研修が終わったのち、OJTとして新規事業開発しているイノベーションセンターに行ってみないかと打診され、これは良い機会だということでSBOM管理ソリューション「Threatconnectome」 1 の開発チームに参加させていただきました。大学院までPythonを使ってコーディングをした経験はあったものの、WebUI系の開発言語に触れたことはほとんどなく、一からのスタートでした。 まずは、自らが望んで行ったOJT先でぶつかった壁について紹介していきたいと思います。 実際の開発業務でぶつかった壁 開発規模が大きい 実際にイノベーションセンターに来て開発業務に携わり、まず最初に感じたことは「あたりまえだがWebアプリ開発研修のときと開発規模が違いすぎる」ということ。 既存機能を修正しようと思ったものの、まず該当のファイルがどこにあるのか分からない。ようやくそれっぽいファイルを見つけてもどこが該当のコードか分からない。該当のコードを見つけてもどう修正すればよいか分からない。とりあえず周りのコードを眺めつつ何とか真似できないかと試行錯誤してみました。修正できたものの単純な修正に1日かかってしまいました(今なら15分くらいで終わる気がします)。 レビュアーにとって分かりやすいコードを書けない 大学院生のときに書いていたコードってめちゃくちゃだったんだと今更反省した記憶があります。あのレベルのコードを業務で書いていたら、製品になりません。 コードの品質をあげるための機能としてPR(Pull Request:開発者のローカルリポジトリでの変更を他の開発者に通知する機能。機能追加や改修など、作業内容をレビュー・マージ担当者やその他関係者に通知し、レビューしてもらう機能)があります。 ここで大事だと感じたのがコードをレビュー側の人間の立場になって書くということ。レビュアーが読んで理解できないコードを書いたり、そもそも読む気にならないコードを書いたりしたらその時点でアウトです。私だと条件分岐が多すぎて複雑すぎるという理由ではねかえされたことがありました。 読みやすいコードを書くって今まで意識してこなかったのですが、これがすごく難しいわけです。自分なりに読みやすく書いてみたつもりでも、これどういう意味?って言われることもしばしばあります。 WebUIの開発において自分の想定と違う挙動になることがある WebUIの開発では自分のイメージしていたものと違う挙動になることが多いと感じました。Pythonでシミュレーションのコードを書いていた時は数値計算のみを行っていたので想定通りの挙動になることがほとんどでしたが、WebUI系のコードは見た目に関わる部分を実装するため想定外の挙動になることが多くありました。例えば、ここのtoggle buttonのこの部分だけ背景色を変えたいなって思っても違う部分が変わるなどがありました。 勉強方法の確立の難しさ 上記の壁にぶつかって感じたのが、やはり知識をつけないことには始まらないということ。どうやら少しだけ調べてみると、より簡潔にコードを書く方法だったり、特殊な挙動をするコードの書き方などがあるようでした。そのような知識がないため、壁にぶつかったときの対処法の手段が乏しく、途方に暮れたことが何度もあったため、手持ちの武器を増やす必要があると感じました。 MetemcyberPJではWebUI側の開発言語としてJavaScript、フロントエンドライブラリとしてReactとReduxというものを用いているため、まずはReactやReduxなどの公式ドキュメントを見てみようと思いました。最初に感じたのは「難しい」。公式ドキュメントは分量が多いうえに全体的に同じテンションで書いてあるため、どこが重要なのかなど初見ではいまいち分かりませんでした。かといって全部読むってなると量が多すぎました(まぁ甘えですが)。 ちなみにReactとReduxに関して本記事の末尾でどんな技術なのかを簡潔にご紹介しております(詳しい内容になるととても収まらない分量になるので割愛します)。 興味のある方はご一読ください。 学習方法を確立する必要性 ReactとReduxの概要を見ると、学生さんなどで使ったことのない方だとなんのこっちゃって感じかと思います。 ただ先に述べた通り、実際にはこれよりはるかに細かい機能や理解しなければならない事柄を製品は多く含み、 かつそれを適切な箇所で適切に利用することで「コードの品質」は向上し、製品として成り立ちます。 少なくとも超一流と呼ばれるエンジニアの方々は当たり前のようにそれらを実践しています(もちろん、開発言語がJavaScriptとは限りませんが)。 さて、では私のように全くコードを書いた経験のない人間はどのように技術に関して理解していかなくてはいけないのでしょうか? そもそもどのような姿勢で学んでいけばよいのでしょうか? 実をいうと学生時代の勉強方法や(多くの学生や新入社員がやりがちな)資格取得のための勉強はあまり効率的な学習方法とは言えないと考えています。 それは現在の(基礎的な)IT技術と呼ばれるものが思っている以上に広範囲であり、かつ各技術が相互に関係しているため、横断的に取得していかなければならないからです。 以上のことから私自身が効率的に知識を習得するために一から学習方法を見直す必要があると感じました。 そのなかで、参考になった学習方法や学習理論、あるいはあまり良くない学習方法と感じたものを紹介していきたいと思います。 学習において大事だと感じたこと 私自身がOJTを通して大事だと思ったあるいは非効率的に感じた学習方法や参考になった学習理論についてご紹介します。 IT技術を体系的に(教科書的に)学ぶことは古い 従来の教科書的な勉強方法はIT業界では古いかもしれないということです。 学生時代は教科書を机の上に広げて、そこに書いてある理論なり公式なりをノートに書き写して学ぶのが一般的かと思います。 資格勉強でも何百ページもある参考書を広げて大事だと思われるポイントをノートにとるなり、マーカーをつけるなりするのかもしれません。 しかしこのような勉強法でIT技術を学ぶにはいくらかの問題点があるように感じます。 まず学生時代の勉強法に関しては、「学校の先生」という強力な解説つきなのと文部科学省の検定済教科書という あらかじめ体系立てて勉強できるように内容(適切な難易度の練習問題があるなど)、分量ともに精査された教材を使っているという点です。 資格勉強に関しても単一の分野(IT分野ならネットワーク、セキュリティなど)を決められた試験範囲内の内容に絞って記述されているだけなので、 実際の業務に直結するかというと実はそうではありません。資格に関しては自身が保持している能力の証明方法の一種であり、勉強の手段としてはあまり適していないと思います。 すなわちこのような勉強法は間違っているとまでは言いませんが、超一流になるためには遠回りすぎる気がします。 事実、ReactやReduxの公式ドキュメントを読んだところで、練習問題があるわけでもないし、そもそも分量が多すぎてよほどの英才でもなければ短時間であれだけを読んでその全体概要を理解するのは困難です。 また先に述べた通りIT技術というのはそのそれぞれが相互に関係しており、複雑につながりあっています。セキュリティ製品を作っているはずなのに、RTKqueryというデータキャッシングの技術を使わなくてはいけなくなったり、alembicというデータベース移行ツールを利用する必要があったりします。つまり極めて広い分野を横断的に習得する必要があり、そのための学習方法として学生時代のやり方などは非効率的であるということです。 効率的な学習方法 さて、では超一流になるための効率的な学習方法とは何なんでしょうか。 少なくとも現段階で私が効果あったと考えているのは、真似ること、恐怖心を持たないこと、(見知らぬ)多くの人と議論を交わすことになります。 まず、真似ることに関してです。多くの開発業務の現場では(開発以外の仕事でもそうですが)いわゆる超一流の方が一定数居ます。その分野の業務に長年従事し精通している方々です。 そういった方々は少なくともそれでお金を稼ぎ、ご飯を食べ続けているのですから、それ相応の成果を出し続けている方々なわけです。 まず (プライドを捨てて) この人たちのやり方を真似ることが重要になります。 はっきりいって公式ドキュメントや資格の勉強を丸一日行うより、1時間そういった方々の業務をみて、真似て作業したほうがはるかに有益かと思います。 よく言いますが、バタフライの泳法を覚えるのに一日中Youtubeでバタフライの動画を見るより、バタフライで泳げる人の真似を1時間した方が良いのと一緒です。 こうすることで、どういった手順で業務にあたり、どういった点に留意し、どういった知識が直接的に必要なのかを体験できます。 次に恐怖心を持たない(怖気づかない)ことが重要になります。 学生の頃や新入社員の頃だと、自分の立場や能力のせいでなかなか意見を言えなかったり、質問ができないという方が多いと思います(少なくとも私はそうでした)。 「こんな初歩的で未熟な質問して見下されないか」、「あの先輩(上司)忙しそうだし質問したらダメだよな」、「こんなこと言ったら(会社内での)自分の立場が悪くなる」 みたいなノイズが入って、理解が不十分なことをそのままにしておいたりタスクを適当にこなそうとするケースが多いのでないでしょうか。 でも考えてみると、 学生に関していうとプロ(超一流、即戦力)レベルの人間なんてごくごく一部のよほど優秀な方以外いません。 なので新入社員なんて(当然私も含めて)ほぼ全員未熟です。未熟で当然なんです。 あと先輩(上司)なんて全員何時でも忙しいです。役職が上がれば上がるほど忙しいはずです。 自分なりの意見を述べて、会社内での立場が悪くなるのだったら、さっさと転職しましょう。 要するに学生だと教授、新入社員だと先輩・上司という「教師」がいるはずなのに、それを利用しないという人間が多いのだと思います。 という風に考えるとそもそもそんなノイズのために自分の理解が不十分なままなのはもったいないというか、愚策であることが分かります。 (1つ注意なのが、あくまで言葉遣いには気を付けるべきかと思います。積極的に質問、意見を通すことと口汚いことは全く別です。) 最後に多くの人と議論を交わすこと。 実際の業務遂行にフォーカスした外部の研修の受講機会や何らかのイベントへの参加を促されたら積極的に挑戦することです。 実際の開発現場にいても、普段は慣れ親しんだ方々としか会話や議論をしません。それもすでにチーム内で凝り固まりつつある価値観のもとでです。 全く出会ったこともない人と議論を交わすことで、想定もしていないような観点からの指摘や意見を受けることができ、新たな知見を得ることが多くあります (学生だと学会がこれにあたるのかもしれません)。 私自身、OJTでMetemcyberに来て、直接的に成長につながったと感じたのは上記の3つです。 ところでこのように学ぶためには実をいうとマインド(学ぶ姿勢)が重要だと考えています。 というのもそもそもの話としてある程度の学習意欲やバイタリティがないと上の3つなんて実行しうるわけがないのです。 超一流になるための学習意欲の根底部分を支えるのはマインドです。 次は、このマインドについて私なりに学ぶことが多かった学習理論を将棋と野球の話を交えてしてみようかと思います。 学習の高速道路 完全に私事なんですが将棋が好きで、さまざまな棋士の対局を見るのが趣味の1つです。棋士の中でも2017年に永世七冠を達成されたまごうことなき大レジェンド羽生善治九段はその人柄まで含めて大ファンなのですが、彼は2006年にIT企業の経営コンサルタント梅田望夫氏のベストセラー「ウェブ進化論」のなかで 「学習(知)の高速道路」という言葉を用いて現代将棋の学習環境の在り様を表現しています[1]。 これはIT技術の進歩とインターネットの普及が進んだことにより、従来は対局機会を得ることが困難であった優れた対戦相手と多くの対戦をこなす機会が得られるようになったことで 誰でも効率的な学習環境で将棋を学ぶことができる、ということを指した言葉です。 しかしその後、羽生九段は「高速道路を走り抜けた先では大渋滞が起きている」とも述べています。これは誰でも簡単に効率的な学習環境の中で学ぶことができるため、ある程度の学習を積んだもの同士の間だと、他者よりも抜きんでることが困難になっていることを表した言葉です。 そして彼は2009年に同じく梅田望夫著「シリコンバレーから将棋を観る」のなかで以下の式を示しました[2]。 $超一流 = 才能 × 対象への深い愛情ゆえの没頭 × 際立った個性$ 右辺の三要素が超一流になるため必要なものであり、なかでも 際立った個性 が重要だと羽生九段は説いています。つまり他者よりも抜きんでるためには誰にもない際立った個性が必要なわけです。そしてこの式は 将棋以外のどの分野にも当てはまる とも述べました。 際立った個性 別の例を1つだけ挙げますと、今や世界的スーパースターであるMLBのロサンゼルス・ドジャース所属の大谷翔平選手も際立った個性を持った人物と言えます。 大谷選手は近代プロ野球において極めて稀な投手と野手を兼任する、いわゆる二刀流の選手です。 当初こそ多くの野球有識者やプロ野球OBの方々は二刀流に関して反対をしていました。 しかしNPBで唯一3度の三冠王を獲得した落合博満氏は大谷選手がプロ野球選手になりたての2013年にNHKのサンデースポーツという番組にて、「二刀流をやることに大賛成。 これほどの 個性 を持った選手は少なくともここ30~40年出てきていない」と述べました。 大谷選手は言うまでもなく類まれな才能の持ち主であり、誰よりも深い野球への愛情を持ち、それでいて誰にも負けない際立った個性があったからこそ、超一流のプロ野球選手になれたのではないでしょうか。 まさに先に羽生九段が述べた超一流の式になぞらえているわけです。 高速道路の高速化 一方で羽生九段は2017年に自身著の「人工知能の核心」のなかで「学習の高速道路を走るなかで、大量の情報を得ることに追われて、かえって自分の頭で課題を解決する時間がなくなっていくことを懸念している」とも述べています[3]。 実をいうと、生成AIが出現してからこの懸念はますます深まったように思います。「AIが何でも課題解決してくれるから自分の頭で考える必要はない」と盲信している方が多いのではないでしょうか。 生成AIの出現以後、学習の高速道路はさらに高速化したように思います。 正直なことを申し上げますと、私自身もいわゆるChatGPT、GitHub Copilotなどはよく利用します。といいますか利用しない日はないといっても良いくらいです。 単純な処理を行うコードやその場に適した文脈などを素早く考えることは自分がやるより、もはやAIにやらせた方が効率的だと感じているからです。 他方で、あくまで出力結果を人間の目で精査する必要はあります。生成AIは要求や質問に対し、優れた回答を素早く出力するところまでは高い精度でできるようになっていますが、 それでも絶対ではありません。さらに言うとそもそも複雑な状況下ではその要求や質問自体ができないケースもあります(AIに正確な指示を与え、期待する出力を得るためのプロンプトを設計・改善するプロンプトエンジニアリングというのはありますが)。 そのため、「AIが何でも課題解決してくれるから自分の頭で考える必要はない」という盲信は些か危険を帯びている気がします。 AIを完全に信じ切ってしまうと、その出力結果の正誤を判断できずに結果として想定と全く違った挙動をおこなうシステムを構築しかねません。 つまり、ここでも知識を十二分に備えたうえで出力結果の正誤を判断し、AIを適切に利用する能力を身に着ける必要があるということです。 言い換えれば、少なくとも現在のIT業界における超一流と呼ばれる人たちは巧にAIを活用できる人材であり、生成AIの使い方まで一流な人間になります。 恐らくこの能力は、IT業界に関わらず、これから先の世の中で超一流と呼ばれるために確実に保持しなくてはならないものだと思われます。 AIを盲信せず、むしろAIを巧みに利用できる人材になる必要があるわけです。 超一流・一流になるために目指すべき人物像 よく学校の先生や会社の先輩、上司がさまざまな事柄(学校なら勉強、仕事なら今自分が従事している業務)に対して 努力・研鑽 せよ、なんて言います。でも思うのですが、上の式の右辺のいずれもが欠けている状況で誰が努力なんてできるのでしょうか? その分野に関する才能もなく、愛情や興味もなく、個性らしい個性もない状況で努力ができるとしたらきっとその人はある意味で人知を超えた強靭な精神力の持ち主だと思います(学校の勉強に関しては多くの場合において、それが強要されてしまっているのが現状ではありますが)。 そういう意味で言うと、3つとも携えるのがもちろん良いのでしょうが、そうはいかないのが現実でもあります。個人的にはできるだけ愛情、興味を持てる分野を探すこととその中で自分だけの個性を作り出すことが重要なのだと感じています。才能に関しては自分で制御できない部分が多いと思っているのが正直なところなので、せめて対象への深い愛情ゆえの没頭と際立った個性を持てるように意識してみるわけです。そうするときっと超一流とまではいかなくとも一流にはなれるのではないでしょうか? NTTコミュニケーションズが与える学習の機会 まず私が感謝したいのはOJTという形でMetemcyberPJに参加させていただいて開発業務の学びの機会を頂いたということ。 先の式において、私自身に自負できるほどの対象への深い愛情があるかは定かではありませんが、好きなこと(興味のあること)を挑戦する機会を提供してくれる会社であることは間違いないです。 1つだけ誤解しないでいただきたいのは簡単な道を選ばせてくれるというわけではないという会社ではないということです。 むしろ自ら険しい山を登ることを奨励し賛同してくれる場所だと感じました。 おわりに 非常に拙くかつ偉そうな文章となってしまいましたが、最後までお読みいただきありがとうございます。 開発系の業務に携わってみたいと思いつつも、いざやってみると難しいことだらけ。それでも今学んでいることがおぼろげながらでも自分の未来につながっているという実感を得ています。 向学心があり、好きなことをとことん突き詰めたいという方はぜひNTTコミュニケーションズの門戸を叩いてみてはいかかでしょうか。 以上で、 NTT Communications Advent Calendar 2024 11日目を終わらせていただきます。 補足資料:ReactとRedux Reactとは Reactは、UIを小さな再利用可能な「コンポーネント」という単位に分割して開発できるフロントエンドJavaScriptライブラリです[4]。 コンポーネントとはUIを構成する要素で、例えばクリックするとユーザー名を表示するという処理を行うコンポーネントなどがあげられます。 また、Reactではコンポーネントごとに「状態(state)」というものを管理し、状態が変更されるたびに自動的にUIが再描画されるという特徴があります。 ここで状態とは、簡単に言うとコンポーネントが持つデータのことです。 例えば、+のボタンを押すとカウンターの数字が1ずつ増えるという簡単なアプリを例にすると、 画面が描画されている状態ではカウンターの数字は0である必要があります。この「カウンターの数字」をReactでは「状態 (state)」と呼びます。 そして、+のボタンが1回押されるごとにカウンターの数字に1を足すという処理を行い、状態を更新する必要があります。 この状態の更新は、状態に変化があるたびに行われます。 Reactは状態とUIの同期が自動で行われるため、手動でDOM操作(ユーザーの操作に応じて動的にコンテンツを更新したりすること)を行う必要がないという利点があります。 では、ChatGPTに提案してもらったJavaScriptのみの場合と、Reactを導入した場合の簡単なコード例を見てみましょう。 JavaScriptのみの場合 <!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title > JavaScript Only Example </ title > </ head > < body > < div > <!-- 初期状態のテキスト --> < p id = "text" > Click the button </ p > <!-- ボタン --> < button id = "button" > Click me </ button > </ div > < script > // ボタンとテキスト要素を取得 const textElement = document . getElementById ( 'text' ) ; const button = document . getElementById ( 'button' ) ; // ボタンのクリックイベント button . addEventListener ( 'click' , function () { // テキストを変更 textElement . innerText = "Hello, World!" ; }) ; </ script > </ body > </ html > JavaScriptのみの場合では、DOM操作する際、 どのタイミングでどのように変更するかを明示的に記述する必要があり、 コードが複雑になりやすいというデメリットがあります。 また、状態が変更された際にUIを手動で更新する必要があります。 // ボタンのクリックイベント button . addEventListener ( 'click' , function () { // テキストを変更 textElement . innerText = "Hello, World!" ; }) ; 本コードでは上記の部分が該当の箇所になります。巨大な開発規模になると、 このように手動でDOMを直接操作するのは非常に大きなコストとなります。 次に同じ処理を行うReactのコードを見てみましょう。 Reactを導入した場合 import React , { useState } from "react" ; function MyComponent () { const [ text , setText ] = useState ( "Click the button" ) ; const handleClick = () => { setText ( "Hello, World!" ) ; } ; return ( <div > <p > { text } < / p> <button onClick={handleClick}>Click me< / button > < / div> ) ; } このコードでは、 MyComponent という名前のコンポーネントを定義しています。 useState("Click the button")という部分にて初期値(初期状態)として「Click the button」という文字列を設定し、 textという変数にその状態を保持させます。 setTextはtextの状態を更新するための 関数 であり、 この場合ボタンをクリックしたときにhandleClickという関数が実行され、そのなかでsetTextが実行されることでtextの状態を更新しています。 今回の場合、setText("Hello, World!")にてtextの状態を「Hello, World!」に更新します。 Reactを使ってみた個人的な感想 最初こそ全く意味が分かりませんでしたが、ある程度の理解ができた後だとなるほどReactを導入したほうが確かに直感的で分かりやすい。 useStateを使った状態管理ができない状況でコードを書くというのがもはや想像できない状況ですらあります。 しかしながら、Reactだけだと各状態はそのコンポーネント内部で完結しており、親コンポーネントや他のコンポーネントと状態を共有するために プロパティ(props)を介して渡す必要があります。これは大規模な開発になればなるほど、状態管理が複雑化することを意味しています。 そのデメリットを克服したのがReduxというライブラリです。 Reduxとは Reduxは、状態をコンポーネント内ではなく、アプリケーション全体で一元管理するためのフロントエンドJavaScriptライブラリです[5]。 Redux自体はReactとは独立した別のライブラリですがReactとの相性が良く、ほとんどの場合においてReactと組み合わせて使われます。 Reduxを導入することでReactのコンポーネントは、 Reduxストア(アプリケーション全体の状態を一元管理するためのオブジェクト)というところから必要なデータを取得し、 状態を変更するためのアクションをディスパッチ(dispatch)するということを行います。 ディスパッチとはアクションをストアに送るためのメソッドのことで、アクションは状態を更新するための指示を含んだオブジェクトになります。 これをストアに送ることで、リデューサー(アクションに基づいてアプリケーションの状態を更新する関数)が実行され、新しい状態が作られます。 このようにReduxでは、状態の変更をReduxストア内のリデューサーによって管理します。 Reduxは状態のグローバル管理するライブラリということになります。 では、こちらもChatGPTに作成してもらった簡単なコードを見てみましょう。 Reactのみを使った場合 import React , { useState } from 'react' ; // Counterコンポーネント:useStateを使ってカウントを管理 function Counter () { const [ count , setCount ] = useState ( 0 ) ; // 状態変数countとその更新関数setCountを定義 const handleIncrement = () => { setCount ( count + 1 ) ; } ; const handleDecrement = () => { setCount ( count - 1 ) ; } ; return ( <div > <p > Count: { count } < / p> <button onClick={handleIncrement}>Increment< / button > <button onClick = { handleDecrement } >Decrement < / button> < / div > ) ; } // Appコンポーネント:Counterコンポーネントを表示 function App () { return ( <div > <h1 > Counter Example ( Without Redux ) < / h1> <Counter / > < / div> ) ; } export default App; このコードでは、Counterコンポーネントというものを含んでいるAppコンポーネントが最初に表示されます。 Counterコンポーネントは、countという変数によって状態を管理し、その状態を表示するものです。 Incrementボタンをクリックすると、handleDecrement関数コンポーネントが呼び出され、 setcountによってcount変数の値が1増えます。同様にDecrementボタンがクリックされると、handleIncrement関数コンポーネントが呼び出され、 setcountによってcount変数の値が1減るという処理を行っています。 状態が更新されるたびに、Counterコンポーネントは再レンダリングされ、新しいcountの値が画面に反映されます。 次にReduxを利用した場合のコードを見てみましょう。 Reduxを導入した場合 // actions.js export const increment = () => ({ type : 'INCREMENT' }) ; export const decrement = () => ({ type : 'DECREMENT' }) ; // reducer.js const initialState = { count : 0 } ; export const counterReducer = ( state = initialState , action ) => { switch ( action . type ) { case 'INCREMENT' : return { ... state , count : state . count + 1 } ; case 'DECREMENT' : return { ... state , count : state . count - 1 } ; default: return state ; } } ; // store.js import { createStore } from 'redux' ; import { counterReducer } from './reducer' ; export const store = createStore ( counterReducer ) ; まず、こちらはアクション(actions.js)、リデューサー(reducer.js)、ストア(store.js)を定義したコードとなります。 今回の場合、actions.jsにてincrementとdecrementという状態を更新するためにディスパッチするアクションを作成する関数を定義しています。 reducer.jsでは現在の状態とアクションを受け取って、新しい状態を返すcounterReducerを定義しており、この場合カウントを増減させるためのロジックを含んでいます。 store.jsのcreateStoreでストアを作成し、リデューサーを引数として渡して状態を管理します。ストアは状態を保持し、 アクションが送られるとリデューサーを通じて状態を更新するという処理が行われます。 import React from 'react' ; import { useSelector , useDispatch } from 'react-redux' ; import { increment , decrement } from './actions' ; function Counter () { // Reduxの状態(store)のcountを取得 const count = useSelector ( state => state . count ) ; const dispatch = useDispatch () ; const handleIncrement = () => { dispatch ( increment ()) ; // INCREMENTアクションをディスパッチ } ; const handleDecrement = () => { dispatch ( decrement ()) ; // DECREMENTアクションをディスパッチ } ; return ( <div > <p > Count: { count } < / p> <button onClick={handleIncrement}>Increment< / button > <button onClick = { handleDecrement } >Decrement < / button> < / div > ) ; } function App () { return ( <div > <h1 > Counter Example ( With Redux ) < / h1> <Counter / > < / div> ) ; } export default App; こちらのコードは実際のUI部分を実装しているコードになります。 Counterコンポーネント内のボタンをクリックすると、handleIncrementやhandleDecrementが呼ばれ、 それぞれincrement()やdecrement()アクションがディスパッチされます。 これらのアクションは、Reduxストアに送信されます。 ストアは、送られてきたアクション(INCREMENTやDECREMENT)に基づいて状態を更新します。 この処理は、counterReducerというリデューサーによって行われることになります。 ストアの状態(count)が更新されると、useSelectorフックを使って最新の状態がCounterコンポーネントに反映され、 画面に表示されるカウントが増減されます。 Reduxを使ってみた個人的な感想 こちらはReactよりも初見はさらに意味が分からないかったというのが正直なところです。 ただ状態管理をグローバルで行うことができるため、MetemcyberPJのように大規模な開発になると使った方が 明らかにコードの保守性や拡張性が向上するのかと思います。 実はこのReduxにはToolkit(公式ライブラリ)としてデータフェッチング(データの取得)、キャッシュ管理を簡潔かつ分かりやすく行う RTKqueryというものもありますが、こちらの内容まで書き出すとものすごい分量になるので、今回は割愛させていただきます。 参考文献 梅田望夫, 『ウェブ進化論 本当の大変化はこれから始まる』, ちくま新書, 2006. 梅田望夫, 『シリコンバレーから将棋を観る―羽生善治と現代』,中央公論新社, 2009. 羽生善治, 『人工知能の核心』, NHK出版, 2017. Meta, 『React Webとネイティブユーザーインターフェースのためのライブラリ』( https://ja.react.dev/learn/describing-the-ui ), 2024. Dan Abramov and the Redux documentation authors, 『Redux A JS library for predictable and maintainable global state management』( https://redux.js.org/tutorials/essentials/part-1-overview-concepts ), 2024. 開発プロダクトの内容に関しては今回の記事の本旨から外れるため割愛させていただきます。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2024 10日目の記事です。 先日、自前のMedia over QUICの実装を IETF 121のハッカソン へ持ち込んで相互接続試験に参加してきました。 その結果、他の参加者の実装との相互接続に成功し、Working Groupのリストに名前を記載いただけました。 本記事では、Media over QUICの概要や動向を紹介し、ハッカソンでの体験について報告します。 はじめに Media over QUICとは? 概要 プロトコルの構成 通信の参加者 何が嬉しいの? メディアのフリーズを防げる 映像や音声以外も扱える オンデマンドとリアルタイムを統合できる サードパーティのネットワークと連携しやすくなる 現在の動向と未来予想 仕様安定化に向けた議論の継続 WebRTCの拡張機能との競合 ハッカソンの参加報告 IETFハッカソンについて ハッカソンにおける取り組み そして延長戦へ 学び おわりに 勉強会の宣伝 謝辞 はじめに こんにちは、イノベーションセンター所属の 前田 です。 WebRTCプラットフォーム「 SkyWay 」のR&Dを担当しています。 最近は、次世代のメディア伝送技術として提案されているMedia over QUIC(以下、MoQ)なども研究しており、同チームの 内田 とともに内製で開発した実装「 moq-wasm 」をOSS公開しています。 先日(2024年11月)、標準化貢献を目的として IETF 121 Dublin のハッカソンへmoq-wasmを持ち込んで参加してきましたので、その様子や成果について報告します。 なお、MoQは現在策定中の比較的新しい概念ですので、本記事ではその概要の解説から行います。 Media over QUICとは? 概要 Media over QUICはその名のとおり、QUICでメディアを伝送するための技術を意味します。WebTransportにも対応しているのでブラウザからでも利用可能です。 プロトコルの構成 プロトコルスタックは以下のとおりです。 MoQのプロトコルスタック(出典: 中間会議資料 ) MoQには大きく分けて以下2つの層が存在します。 Media over QUIC Transport(以下、MOQT) ストリーミングフォーマット MOQTの層ではメディアを送信するための共通のルールや振る舞いが提供されます。 MOQTのドラフト を確認すると、メッセージの種類やフォーマットなどが定義されていることがわかります。 ストリーミングフォーマットの層ではMOQTの上でどのようにメディアを扱うかが定義されます。例えば、 WARP というストリーミングフォーマットでは以下のことが定められています。 WebCodecsを活用した LOC というメディアパッケージ手法を主に採用すること (扱うメディアは基本的に音声・映像のみ) 送信するメディアの情報を相手へ伝えるためにCatalogという仕組みを用いること この記事を書いている2024年12月現在、WARPはまだ個人ドラフトの状態ですが、もうすぐ正式にWorking Group(以下、WG)ドラフトになる 1 予定です。 通信の参加者 MoQの通信には主に以下3種類の参加者が存在します。 Original Publisher: メディアの送信を開始する Relay: メディアを受信し、(必要に応じてキャッシュし、)それを他の参加者へ転送する End Subscriber: メディアを受信し、それを他の参加者へ転送しない Original / Endというワードが必要なのは、RelayがOriginal Publisherに対するSubscriberとなり、End Subscriberに対するPublisherとなるため、それらと区別するためです。 もしもメディアを双方向に送り合いたい場合、アプリケーションの中でメディアごとにOriginal PublisherとEnd Subscriberを入れ替えれば実現できます。 Relayの数は定められていません。アプリケーションのユースケースやその規模などに応じてRelayの台数を増やすことでスケーリングできます。 何が嬉しいの? MoQは主に以下の2つのユースケースに対し、柔軟性を高めながらカバーしていくことが期待されています。 HLS(HTTP Live Streaming)などを用いるようなメディアコンテンツ配信 WebRTCなどを用いるようなリアルタイムコミュニケーション 具体的には以下が主なメリットだと思っています。 メディアのフリーズを防げる 主にHLSなどTCPベースの配信手段を活用する場合、パケットの送信に失敗すると次のパケットの送信を待たせて先に失敗したパケットを再送します。結果、パケットの再送が成功するまで後続のパケットが詰まってしまい、再生しているメディアがフリーズしてしまいます 2 。 QUICはTCPとUDPの特性を併せ持っているため、パケットの送信に失敗してもそのパケットの再送を待たずに後続のパケットの送信を行えます。これによって、ネットワーク障害などが発生している場合を除き、再生中のメディアのフリーズを抑えることができます。 映像や音声以外も扱える 現在最も議論が進んでいるストリーミングフォーマットは音声・映像を扱うWARPですが、それ以外のストリーミングフォーマットを定義することで全く違う種類のメディアを送信することもできます。 例の1つとして、MoQ上でチャットアプリケーションを実現する方法が 個人ドラフト として提案されています。 また、過去に別のWGの会議の中でXRに使用する3Dデータの送信手段の案の1つとしてMoQが言及されたこと 3 などもあり、将来的に幅広いユースケースで活用される可能性があります。 オンデマンドとリアルタイムを統合できる これまでは、HLSのようなオンデマンド性とWebRTCのような高いリアルタイム性を両立した形でメディアコンテンツ配信を実現できるプロトコルが存在しませんでした。 一方、MoQは必要に応じてRelayにキャッシュを持たせることができ、WebRTCと同程度の遅延で送信できるため、これを実現できます。 過去の映像の再生中に、シームレスに現在の映像へ切り替えるような使い方も可能です 4 。 サードパーティのネットワークと連携しやすくなる MoQは標準化の範囲にCDNの領域を含んでいることも大きな特徴の1つです。MOQTのドラフトの中では、アプリケーションから独立したサードパーティのネットワークを活用できるようになることを目標の1つとして定義しています。 また、送信されるメディアの秘匿性が保たれたまま転送されることを前提として定義していることもあり、これまで以上にCDNの選択・連携がしやすくなると予想されます。 現在の動向と未来予想 まず結論から述べると、MoQが広く活用され始めるまでにはまだまだ時間がかかると思っています。 理由は大きく分けて2つあります。 仕様安定化に向けた議論の継続 MoQ WGはIETF以外にも中間会議で議論を進めています。中間会議は2週間に1度開催されており、回によっては2日連続で6時間以上の会議を開催することもあります 5 。 MoQ WGの今後の会議予定日 また、現在のMOQTのドラフトの最新バージョンは07であり、まだ比較的若い状態だと思われます。さらに、ストリーミングフォーマットは一番議論が進んでいるWARPすらWGドラフトになっていません。 これらを踏まえると、仕様が安定するまでに今後も多くの議論が必要になると予想されます。 WebRTCの拡張機能との競合 現在リアルタイムコミュニケーションの用途で広く活用されているWebRTCは、あらゆるユースケースへ対応するために機能の拡張が進められている最中です。 一例として、扱うメディアの柔軟性を向上できるRTPTransportやリアルタイム配信用途で活用しやすくするためのWHIP / WHEPなどが提案され、各プラットフォームで実装が進められています。 今後もこのような拡張が続くことで、リアルタイム通信の領域においては、ある程度のユースケースはWebRTCでカバーできるようになっていくことが予想されます。 以上の理由から、MoQは仕様が定まるまでに数年かかり、その後特定のユースケースに向けて徐々に市場に浸透していくような流れになるのではないかと考えています。 特に、大規模な音声・映像配信のユースケースにおける活用が先行するのではないかと予想しています。 ハッカソンの参加報告 ここからはハッカソンの参加報告と題してIETFにおけるハッカソンの概要や私の取り組みをご紹介します。 IETFハッカソンについて IETFハッカソンはIETFの最初の2日間の土日で開催されます。ハッカソンでは特定の技術の専門家が一箇所に集まり、アイディア出しや実装、相互接続試験などを行います。 ハッカソンの参加方法はオンサイトとリモートの2通りから選べます。 オンサイトの会場にはいくつものテーブルが用意されており、特定の技術に興味のある人が集まったら入口付近に貼られたボードへ技術名を書くことでテーブルを確保します。 テーブルボード(MoQ WGは#10を確保) 現在、MoQ WGの取り組み内容は特に指定されておらず、参加者は自由に議論したり自分のタスクに取り組んだりできます。 MoQの専門家だけではなくQUICやWebRTCなど関連技術の専門家が集まったり、あるいはたまに訪れてくださるので、その都度興味深い話を聞くことができます。 また、ハッカソン期間中に相互接続試験が行われています。もしも自分の実装を持っていれば試験に参加できます。 相互接続試験の結果はハッカソンの終わりではなくMoQ WGの会議の初めに発表されます。 ハッカソンにおける取り組み 私はオンサイトでハッカソンに参加し、moq-wasmの実装を持っていたため相互接続試験に取り組みました。 相互接続試験は参加者が実装した MoQ をそれぞれ接続するものです。 Relayの実装を持っている参加者はRelayサーバのURLを公開し、Original Publisher / End Subscriberの実装を持っている参加者は公開されたRelayサーバへの接続を試みます。 moq-wasmはRelay / Original Publisher / End Subscriberのすべてを実装しているため、Relayサーバの公開と他の参加者の繋ぎこみの両方を行いました。 これらの相互接続試験に関するやり取りはその場で、もしくはWGのSlack上で行われます。 RelayサーバのURLはSlackで公開されますが、基本的にはすぐに接続に成功せず、その場やSlack上でコミュニケーションを取りながら問題のある箇所を探します。 私の場合、オンサイト会場にいたLorenzo Miniero氏からいくつかのご指摘をいただき、問題を修正することで相互接続を成功させることができました。 また、他の参加者から報告された接続失敗の結果を分析してフィードバックすることで、その方の実装の修正に貢献できました。 結果として、いくつかの実装との接続に成功したため、WGの相互接続試験リストに「NTT(Tetta)」と名前を載せていただき、MoQ WGの会議の中で報告いただけました。 相互接続試験の結果 6 (出典: 会議のアーカイブ動画 ) そして延長戦へ 実は会議の中で報告された結果は私が満足できる結果ではありませんでした。というのも、最終的な試験表はIETF開催の約2週間前にリリースされたドラフトバージョン07ベースの結果を元に作成されましたが、私が持ち込んだのはバージョン06ベースの実装だったため一部のメッセージしか疎通しなかったという結果となったためです。 (ハッカソンの終盤で07へのバージョンアップに取り組みましたが、他の参加者との接続確認は完了しませんでした) ハッカソンの終わり際に、MoQ WG ChairのAlan Frindell氏に上手くいかず悔しかった旨を伝えたところ、なんと「後日集まって延長戦をやろう」と言ってもらえました。 私はこれを非常に嬉しく思い、IETFの会議の合間に準備を進めました。 そして、IETF 6日目の木曜日にThe Forumという名の部屋 7 に集まり延長戦を行いました。 結果として、メディアを送信するためのメッセージを含め、上記の表における「moxygen (Alan, Daniel, ...)」のServer実装や「Meetecho (Lorenzo)」のClient/Server実装と一通り疎通を確認できました。 学び 相互接続試験では各参加者の実装方法や実行環境が異なるため、MoQの領域ではないところでいくつかの問題が生じました。私の場合はQUICやセキュリティに関する箇所で問題が発生し、解析や修正に時間がかかりました。この経験から、MoQだけではなく関連技術についても理解を深めることが重要だと学びました。 おわりに 今回はMoQ WGの相互接続試験に初めて参加し、いくつかの実装と相互接続できることを示したり、他の参加者の実装を改善した形で貢献できました。 今後もmoq-wasmのメンテナンスを続け、さらなる貢献を目指していきたいと考えています。 勉強会の宣伝 現在MoQは非常に議論が活発で、仕様もまだまだ大きく変わるので追いかけるのは大変だと感じています。 不定期で 勉強会 を開催しておりますので、MoQに興味があるという方は気軽にご参加ください。 なお、先日開催された勉強会では以下の資料で登壇しました。特にメッセージやシーケンスについて詳しく解説しています。 詳細が気になる方はぜひご確認ください。 謝辞 I would like to extend my gratitude to Alan, Lorenzo, and all the others who helped me. Thank you so much! 何か提案がある場合、まずは個人ドラフトとしてWGに提出し、WG全体で扱うべきと判断されたらWGドラフトになります。 ↩ この問題をHead of Line Blockingと呼びます。 ↩ MOPS WGの 過去の会議 で言及されています。 ↩ WGではこの特徴のことを「Live Edge」と呼んでおり、現在具体的な実現方法について議論中です。 ↩ これにより多くのことが中間会議で決まってしまうことを避けるため、QUIC WGの進め方の一部を取り入れてコンセンサスを得ることがIETF 121で提案されました。 ↩ 空欄は接続試験に失敗したことを表しているとは限りません。今回参加しなかった方の項目や、サポートしている伝送方法(Raw QUICベースかWebTransportベース)の違いにより試験できない項目を含んでいます。 ↩ IETF期間中は基本開放されている大広間であり、休憩スペースとして利用したり参加者同士でコミュニケーションを取る場として活用できます。 ↩
アバター
こんにちは、NTTコミュニケーションズの 現場受け入れ型インターンシップ2024 に参加した大学2年生の大堀です。X(旧:Twitter)では @LimitedChan (限界ちゃん)というハンドルネームでサイバーセキュリティに関して活動しています。 普段は大学で情報系の学習をしながら、個人的に脅威インテリジェンスをはじめとしたサイバーセキュリティに対して興味があり、さまざまな学習に取り組んでいます。 2024年8月26日〜9月6日までの2週間、「脅威インテリジェンスを生成・活用するセキュリティエンジニア/アナリスト」としてNetwork Analytics for Security(通称:NA4Sec)プロジェクトに参加しました。 この記事では、私がインターンシップで取り組んだ内容について紹介します。 Na4Secプロジェクトとは? インターンシップ参加の経緯 インターンシップ概要 今回作成したもの 環境説明 環境構築 Elasticsearch + Kibana + Fleet Server編 OpenCTI編 Elastic Agent編 Cobalt Strike - OpenCTIでの分析 OpenCTIでの分析 Cobalt Strikeのレポート作成 Cobalt Strike - EDRでの検知 データ構造の変換 Streamの設定 アラートルールの設定 Windowsから通信を発生させてみる 学んだこと おわりに Na4Secプロジェクトとは? 私が参加したNA4Secプロジェクトは「NTTがインターネットの安全性を守る社会的責任を果たす」という理念をもとに、「攻撃インフラの特定と撲滅」に挑むプロジェクトです。このプロジェクトには、NTT Comイノベーションセンターを中心に、NTTセキュリティ・ジャパンやエヌ・エフ・ラボラトリーズのメンバーが集結し、日夜攻撃インフラを追跡しています。 また、このプロジェクトのもう1つの特徴として、NTT Comグループ内における脅威インテリジェンスチームの役割を担っています。有事において脅威インテリジェンスを提供し、意思決定を支援することで、NTT Comグループのセキュリティを強化しています。 インターンシップ参加の経緯 私がこのインターシップへ応募した理由は、今までに経験したことが実務ではどのように活かせるかを体感したいと思ったためです。今までセキュリティ・キャンプ全国大会などのイベントへの参加や日々の学習、記事執筆を通じて知識を深めてきました。このインターシップを通して、自分のスキルと実務で求められるスキルのギャップを確認し、さらなる成長を目指したいという意欲もあり、応募を決めました。 インターンシップ概要 インターンシップでは1週目と2週目に大きく分けて2つの活動を経験しました。1週目では、もう1人のインターン生とともに業務をし、2週目ではそれぞれに合ったテーマを決めていただき業務をさせていただきました。 1週目 脅威調査(Cobalt Strikeとその悪用) 脅威探索(Cobalt Strike C2サーバに関して) 脅威情報発信(フィッシングサイトに関して) 2週目 TIP + EDR + SIEMの基盤システム構築と運用 今回は2週目の業務内容について紹介していきます。 今回作成したもの ユーザ端末が危険性のあるIPアドレスに接続してしまうなどといった事象を検知し、アラートを生成するための基盤システムを作成しました。脅威情報を登録し、収集・分析・共有するTIP(Threat Intelligence Platform)、ユーザ端末上で脅威を検知するEDR(Endpoint Detection and Response)、情報を管理しアラート通知するSIEM(Security Information and Event Management)の3つから構成されます。 環境説明 今回の環境ではTIPとしてOpenCTI、SIEMとしてElasticsearch、Kibana、Fleet Server、EDRとしてElastic Agentを使用しました。それぞれのツールについて簡単に説明します。 ツール名 説明 Elasticsearch オープンソース型の分散型検索エンジン。大量のデータから全文検索や分析、リアルタイムでのデータ処理を効率的に行う。 Kibana データの可視化ツールとして、Elasticsearchと連携し、ダッシュボードやグラフを作成してデータを視覚的に確認する(①)。 OpenCTI サイバー脅威インテリジェンス(CTI)システムで、脅威情報を収集・分析・共有するためのプラットフォーム。Elasticsearchをデータストレージとして利用する(②)。 Fleet Server Elastic Agentを中央で管理および監視する(③)。 Elastic Agent ホスト上でログなどを収集するElasticのエージェントツール。拡張機能を利用することで、EDRとしての機能を担い、ログデータをElasticsearchへ送信する(④)。 これらを利用して以下のように構築しました。 上記の環境では、Windows環境はユーザ端末、Ubuntu環境はSOC(Security Operation Center)の役割を模擬したものになります。SOC側で収集・分析した脅威情報がWindows端末のEDR機能に反映されることによって、リアルタイムで脅威を検知する仕組みとなっています。 環境構築 Elasticsearch + Kibana + Fleet Server編 GitHub上の公開リポジトリ *1 にあるDocker Composeファイルを利用し、Elasticsearch + Kibana + Fleet Serverをセットアップしました。 また、環境に合わせて各種env, ymlファイルを編集し、Elasticsearchに対してHTTPS接続するために証明書を設定しました。 構築を終えたあと、実際にKibanaUI(localhost:5601)に接続をしてみると以下のような画面が表示されます。 ここから、データを可視化したり検索したりできます。 OpenCTI編 こちらもGitHub上で公開されているDockerイメージ *2 を利用し、OpenCTIをセットアップしました。 また、公式ドキュメントを参考にしてenvファイルを作成し、ElasticsearchとHTTPSで連携するために証明書を設定しました。構築を終えたあと、実際にOpenCTI(localhost:8080)に接続をしてみると以下のような画面が表示されます。ここから、脅威情報を収集したり分析したりできます。 設定を終えた後、初めのうちはOpenCTIにアクセスできましたが、時間を開けると立ち上がらなくなる問題が発生しました。調査したところ、Elasticsearch + Kibana + Fleet Serverの環境を構築したときのymlファイルにElasticsearchの設定がすでに記載されていたため、OpenCTIのymlファイルに記載されているElasticsearchの設定と競合していたのが原因だと判明しました。そのため、OpenCTIのdocker-compose.ymlファイルからElasticsearchの設定を削除することで解決しました。 また、便利な機能として、OpenCTIはConnectorと呼ばれるプラグインを使うことで外部のインテリジェンスを自動で取り込むことができます。より効率的にインテリジェンスを収集したかったため、ymlファイルを編集して「mitre」「cve」「malpedia」の3つのプラグインも導入してみました。 Elastic Agent編 次にEDR機能を担うElastic Agentを構築し、Intergration(拡張機能)としてElastic Defendを導入しました。 Intergrationは必要なコマンドをWindows上で実行するだけで導入でき、設定はKibanaUI上で行えます。 Cobalt Strike - OpenCTIでの分析 おおかたの環境構築が終わったところで、1週目で得たCobalt Strikeに関する脅威情報をOpenCTIに登録し、分析をしてみました。 Cobalt Strikeとは Cobalt Strikeは、企業のセキュリティテストを行うために作られた正規のツールです。しかし、実際に攻撃に利用できることから、不正に改造されたバージョン(クラック版)が出回っています。サイバー攻撃者はこのクラック版のCobalt Strikeを悪用している事例が多く見られており、問題になっています。 OpenCTIでの分析 1週目では、Cobalt Strikeが実際に悪用された事例をダイヤモンドモデル *3 と呼ばれる手法を用いて分析しました。今回はその中の1つの事例をピックアップしてOpenCTIに登録し、さらなる分析をしてみました。 ここからは、OpenCTIでの分析を詳しく説明していきます。 OpenCTIに登録する際には、STIX2.1 *4 と呼ばれる脅威情報を表現するための標準化されたデータモデルを使って情報を登録する必要があるため、そのデータモデルに従って情報をSTIXオブジェクトとして登録しました。 今回はCobalt Strikeに関連したマルウェア、関連ファイル、URL、ドメイン、C2サーバなどの観測値(Observables)がSTIXオブジェクトとして登録されています。 Cobalt Strikeのレポート作成 STIXオブジェクトを登録したあとは、OpenCTIの機能を使って脅威情報をまとめた「レポート」を作成しました。 ここで説明する「レポート」とはSTIXで定義されている概念の1つで、特定のトピックに関する脅威情報の集合体を指します。「レポート」では、個々のSTIXオブジェクトのほかに、STIXオブジェクト同士の関係性を記述できます。 OpenCTIでは、それらの関係性をグラフ構造で図示できます。 以下の図は、1週目に調査したCobalt Strikeを悪用した事例の1つを「レポート」化したものになります。 図に表してみることで、Cobalt Strike単体での攻撃がメインではなく、他のマルウェアや悪意のあるファイルと組み合わせて攻撃が行われていることが俯瞰的に把握できるようになりました。 Cobalt Strike - EDRでの検知 OpenCTIに登録したCobalt Strikeに関する脅威情報をもとに、EDRであるElastic Agentを使用して脅威を検知してみました。今回は、Cobalt StrikeのC2サーバで使われたIPアドレスをインジケーターとして登録し、Windows上でそのIPアドレスに接続があった場合にアラートを出すようにKibana上から設定しました。 以下がEDRでの検知の大まかな流れです。 1.不正なIPアドレスに接続 Windows上でCobalt StrikeのC2サーバのIPアドレスに接続を試みる。このとき、登録されたインジケーターIPアドレスへの接続試行がElastic Agentで検知される。 2.イベントの収集と送信 Elastic Agentの検知がイベントデータとして記録され、Fleet Serverを介してElasticsearchに送信され、データベース上に保存される。 3.Kibanaでのアラートの発生 Elasticsearchに保存されていたイベントデータがKibanaのアラートルールにマッチし、アラートがトリガーされてダッシュボードに表示される。 (4.)OpenCTIでの脅威インテリジェンス管理 OpenCTIもUbuntu端末上で稼働しているため、Kibanaでアラートが発生した際にKibanaと同時にOpenCTIでも脅威情報を確認できる。 データ構造の変換 OpenCTI上のデータ構造とKibana上のデータ構造が異なるため、そのままでは利用できません。そのため、Kibanaのアラートルールで使用されているECS(Elastic Common Schema) *5 を適用するため、OpenCTIのデータ構造をECSに変換する必要があります。この変換のために、OpenCTIにElastic Connectorを導入しました。これはOpenCTIのComposeファイルにElastic Connectorの情報を記述することで導入できます。 Streamの設定 OpenCTIとKibana間でリアルタイムにデータをやり取りするため、Streamを設定しました。StreamはOpenCTIとKibana間でのデータを送受信するための機能です。OpenCTI上で設定することで、Kibanaでデータを受信できます。 アラートルールの設定 アラートルールはKibanaUI上で設定できます。ECSのリファレンスやOpenCTIが出しているドキュメントを参考に、以下のように設定しました。 Windowsから通信を発生させてみる 以下の図に示されているのは、Windows端末からUbuntuサーバのIPアドレスに対する通信がアラートとして検知されたものとなっています。 本来は、Cobalt StrikeのC2サーバへの通信を検知することを目的としていました。しかし、検証環境がクローズドなためCobalt StrikeのC2サーバのIPアドレスへの通信を発生させにくいという問題が発生しました。そのため、代替案としてUbuntuサーバのIPを用いた検証を実施しました。この手法によりアラートの検出ロジックが正常に機能していることを確認しました。 原理的には、STIXオブジェクトとして登録したCobalt StrikeのC2サーバのIPアドレスに対する通信が発生した場合も、同様に検知可能であることが期待されます。 学んだこと 今回のインターンシップを通して、以下のようなことを学べました。 環境構築について 自分で環境を構築したり機能を追加したりすることで、システム同士の連携やデータのやり取りの仕組みを理解できました。今まで、すでに完成されている環境やツールを使用することが多かったので、自分の手を動かすことの大切さも学べました。 また、今回の場合インターネットで検索してもなかなか情報が見つからないことが多かったため、公式ドキュメントを読むことの重要性を再認識しました。 構築時には多くのトラブルシューティングに直面して、心が折れそうになることもありました。しかし、エラーログや公式ドキュメントを確認し、それでも原因がわからない場合は積極的に質問することで解決ができました。この経験を通じてコミュニケーションの重要性を学べました。さらに、セキュアな環境構築の方法も教えていただいたので、これからの環境構築で意識していきたいと思いました。 脅威インテリジェンスについて 実際に脅威インテリジェンスを用いてEDRを運用することで、脅威インテリジェンスがサイバーセキュリティにおいて重要なものであると再認識しました。 また、普段から脅威情報に関するレポートや記事を積極的に読むことは大切だと感じました。今回のインターンシップでは、特にCobalt Strikeの脅威分析に関するレポートを読むことで、その脅威に対する知識を深められました。 おわりに 普段ではなかなか経験できないようなことを体験でき、非常に充実した2週間でした。脅威インテリジェンスに関する知識を深められただけでなく、実際にその知識を活かせたことがとても嬉しかったです。 この貴重な機会を与えてくださったNA4Secプロジェクトの皆さま、本当にありがとうございました。また、神田さん、鮫嶋さん、益本さん、坪井さん、皆川さん、大変お世話になりました。ありがとうございました。
アバター
この記事は、 NTT Communications Advent Calendar 2024 9日目の記事です。 この記事では、SkyWayを使ったアプリ開発が爆速になるCLIツールを作った話を紹介します。 CLIツールにどのような機能を実装したのか、機能を実装する際にどのようなことを考えたのかについて、詳しく説明します。 はじめに 注意事項 skyway-cliの機能 skyway-cli channel watch skyway-cli token serve skyway-cliを設計するときに考えたこと Unix哲学に則ること 設定値を柔軟に変えられること skyway-cliを実装するときに考えたこと インストールを簡単にする テストを書きやすくする おわりに 参考情報 はじめに 皆さまこんにちは。イノベーションセンター SkyWay DevOps プロジェクト所属の @sublimer です。 SkyWay は、ビデオ・音声通話機能を簡単にアプリケーションに実装できる、リアルタイムコミュニケーションを実現するためのプラットフォームです。 SkyWayではこれまで、JavaScript、iOS、Androidの各プラットフォーム向けにSDKを提供してきました。 今年は、これら3つのプラットフォームに加えて、Linux向け、Unity向けのSDKも提供を開始しました(Unity向けSDKは現在ベータ版です)。 また、録音・録画機能を利用するためのRecording API、SkyWayのChannelの作成と情報の参照をするためのChannel APIの提供も始めました。 このようにSkyWayではさまざまな機能をどんどんリリースしているため、これまで実現が難しかった機能も簡単に実装できるようになっています。 一方で、機能が増えることでSkyWayを使ったアプリの開発をいかに効率よく進めていくかが課題となります。 そこで、SkyWayを使ったアプリ開発がより効率的に進められるようにするためのCLIツール、「skyway-cli」を作成しました!! github.com 注意事項 今回開発したCLIツールは公式として提供している機能ではありません。 あくまでも個人的に開発したものであり、SkyWay公式のサポートは提供していません。 利用される場合は、公式のツールではないことをご理解の上でご利用ください。 もし不明点や不具合があった場合は、GitHubリポジトリのissueとして連絡いただければ、できる限り対応いたします。 skyway-cliの機能 skyway-cliには、以下の11個のサブコマンドが実装されています。 skyway-cli token: SkyWayの認証情報であるSkyWay Auth Tokenを生成します。 --admin フラグをつけると、SkyWay Admin Auth Tokenを生成します。 decode: SkyWay Auth Token、SkyWay Admin Auth Tokenをデコードします。 --pretty フラグをつけると、デコード結果を整形して表示します。 serve: SkyWay Auth Tokenを払い出すためのHTTPサーバーを起動します。 verify: SkyWay Auth Tokenが有効なものかどうかを検証します。無効なパラメーターが入っている場合、エラーとしてその情報を出力します。 channel create: Channel APIを用いて、SkyWayのChannelを作成します。 find: Channel APIを用いて、SkyWayのChannelを検索します。 get: Channel APIを用いて、SkyWayのChannelの情報を取得します。 watch: SkyWayのChannelにおいて発生したイベント情報をリアルタイムで取得し、表示します。 recording start: Recording APIを用いて、SkyWayの録音・録画を開始します。 stop: Recording APIを用いて、SkyWayの録音・録画を停止します。 get: Recording APIを用いて、SkyWayの録音・録画の情報を取得します。 これらのサブコマンドのうち、特に便利なものを2つ紹介します。 skyway-cli channel watch このコマンドを使うと、以下の例のようにSkyWayのChannelにおいて発生したイベント情報をリアルタイムで取得し、表示できます。 (実行結果はあくまでも説明用のものです) $ skyway-cli channel watch --id 0ea5bcde-a6f4-4931-9223-f5fdace5ec78 {"type":"MemberAdded","data":{"channel"... // Memberの入室 {"type":"StreamPublished","data":{"channel"... // Publicationのpublish {"type":"StreamUnpublished","data":{"channel"... // Publicationのunpublish {"type":"MemberRemoved","data":{"channel"... // Memberの退室 このコマンドを使うことで、SkyWayのChannelへのMemberの参加・退出の情報や、Publication、Subscriptionのpublish、subscribeの情報を、手軽にリアルタイムで確認できます。 内部の実装としては、RTC-APIサーバーにWebSocketで接続し、指定したChannelのイベント情報を受信して表示しています。 通常、この処理はSDK内部で行われますが、SDKにおいて実行している処理と同等の処理をCLIツール内で実装することで、CLIツールからもSkyWayのChannelのイベント情報をリアルタイムで取得できるようにしました。 なお、SkyWayでは、通信状況を記録、可視化するための Analytics機能 を提供しています。 Analytics機能は、過去のデータも含めてWebブラウザ上で簡単に通信状況や品質を確認できます。 運用時に過去の通信状況を確認したい場合はAnalytics機能、アプリ開発時に手軽にリアルタイムで通信状況を確認したい場合は skyway-cli channel watch コマンドのように、状況に合わせて使い分けることをおすすめします。 skyway-cli token serve このコマンドを使うと、デフォルトでは8080番ポートでHTTPサーバーを起動し、SkyWay Auth Tokenを払い出すAPIが利用できるようになります。 $ skyway-cli token serve ⇨ http server started on [::]:8080 通常SkyWay Auth Tokenは、ユーザーのアプリケーションの認証認可ロジックを踏まえて適切な権限を設定して払い出す必要があります。 こうすることで、セキュアなアプリケーションを実装できますが、「ちょっと動作を確認できればOK」という場合には払い出し用のサーバーを作るのが少し手間になってしまいます。 SkyWay Auth Tokenの生成はフロントエンドでも実行可能ですが、シークレットキーをフロントエンドのソースコードに書き込むのはセキュリティ上好ましくありません。 skyway-cli token serve コマンドを使うことで、フロントエンドのソースコードにシークレットキーを書き込まずに、手軽にSkyWay Auth Tokenを払い出すAPIを利用できるようになります。 skyway-cliを設計するときに考えたこと 今回skyway-cliを設計するにあたって、以下の2つのポイントを考慮しました。 Unix哲学に則ること 設定値を柔軟に変えられること Unix哲学に則ること skyway-cli token decode コマンドを例に説明します。 このコマンドは、標準入力からSkyWay Auth Tokenを受け取り、デコード結果を標準出力に出力します。 エラーがあれば、標準エラー出力にエラーメッセージを出力し、終了ステータス 1 で終了します。 この挙動は、「過度の対話的インタフェースを避ける」、「すべてのプログラムをフィルタにする」というUnix哲学に則っています。 SkyWay Auth Tokenの入力方法には、標準入力から受け取る方法以外にも、ファイルとして読み込んだり、対話型のプロンプト経由で読み込む方法が考えられます。 これらの方法でも入力を受け付けることはできますが、標準入力から受け取る方法は他のプログラムの出力をパイプで受け取ることもでき、対話型のプロンプトのルールを覚える必要もありません。 このように、シンプルで自動化もしやすい点が、標準入力からデータを受け取ることのメリットです。 もちろん、 --input-flie のようなオプションを追加してファイルからの入出力に対応することもできますが、リダイレクトやパイプを使えば必ずしもコマンド側でファイルの入出力ができるようにする必要はありません。 このような、「Worse is better(劣る方が優れている)」の考え方もCLIツールを設計する際には重要だと考えています。 設定値を柔軟に変えられること skyway-cliは、コマンド実行時のパラメーターを以下の3つのいずれかの方法で受け取ることができます。 設定ファイルに記述された設定値 環境変数に設定された設定値 コマンドライン引数で指定された設定値 通常は設定値を明示的に指定する必要はなく、設定ファイルに記述された設定値を利用します。 一時的に設定値を変更したい場合は、環境変数やコマンドライン引数で指定できます。 SkyWayでは、アプリケーションIDとシークレットキーの2つの認証情報を使ってSkyWay Auth Tokenを生成します。 これらの認証情報は、制限の範囲内であれば複数払い出すことができます。 ユーザーによっては、開発用、試験用、本番環境用のように、複数の認証情報を使い分けたいケースが想定されます。 複数の設定値を柔軟に変えられるようにしておけば、普段は設定ファイルに書かれた開発用の認証情報を使い、試験や本番環境の不具合調査の時だけ環境変数で試験用の認証情報を指定するといった使い方ができるようになります。 skyway-cliを実装するときに考えたこと skyway-cliを実装するにあたっては、以下の2つのポイントを考慮しました。 インストールを簡単にする テストを書きやすくする インストールを簡単にする CLIツールを開発するにあたって、インストールのしやすさは非常に重要です。 インストール方法は、以下のようなやり方が考えられます。 OSごとのパッケージマネージャー経由でインストールできるようにする GitHubリリースページからバイナリをダウンロードして使う プログラミング言語ごとのパッケージマネージャー経由でインストールできるようにする 1つめの方法は、APTやHomebrewを使う方法ですが、配布のための追加の作業が必要になるため、学習コストなどが発生します。 2つめの方法はシンプルで配布の自動化もしやすいですが、OSによってはダウンロードしたバイナリファイルの実行がブロックされたり、ユーザーにパスの設定をしてもらう必要があるなど、ユーザー側に手間が発生します。 3つめの方法は、プログラミング言語のランタイムを準備してもらうというユーザー側の手間が発生しますが、メジャーなプログラミング言語であればそこまでハードルは高くないと考えられます。 また、パッケージマネージャーにはインストールだけでなくバージョン管理やアップデートの機能も入っているため、ツールの配布の観点では非常に使いやすい仕組みです。 以上を踏まえ、今回は3つめの方法を採用しました。 また、利用するプログラミング言語はGoを採用しました。 GoでCLIツールを実装して配布することには、以下のようなメリットがあります。 OSごとの差分をある程度吸収できる パッケージレジストリへの公開が不要 CLIツールを作るための実績のあるライブラリがある Goは、各OS向けのバイナリを容易にビルドできます。 CLIツールは複数のOSに対応するのが望ましいため、移植性の観点が重要となります。 また、GoはGitHubリポジトリをパッケージレジストリとしてそのまま利用できるため、パッケージレジストリへの公開の処理が不要です。 これにより、CI/CDパイプラインでのリリース処理を省略でき、GitHubリポジトリにpushするだけで容易にリリースができるようになります。 加えて、実績のあるCLIツール開発用ライブラリがある点もGoを選択した理由です。 CLIツール開発用のライブラリには、 Cobra を利用しています。 Cobraは、KubernetesのCLIツールである kubectl やGitHubのCLIツールである GitHub CLI など、多くのCLIツールで採用されているライブラリです 1 。 CLIツールを作るための技術選定にはあまり自信がなかったので、今回は実績のあるライブラリを選択しました。 CLIツールをGoで実装したことで、 go install コマンドを実行するだけで簡単にインストールができるようになり、さまざまなOSにも対応している利便性の高いCLIツールを実装できたと考えています。 テストを書きやすくする 前述の通り、CLIツールは最終的な入出力が標準入出力であるため、そのままではテストが難しくなります。 CLIツールのテストが難しくなる要因は、入出力のインターフェースが標準入出力である点だと考えています。 入出力のインターフェースが標準入出力となっている場合、stdinやstdoutをモックする必要があり、テストの実装コストが高くなります。 ただし、標準入出力も含めてE2Eテストのようなことをする必要は必ずしも無く、CLIツールの内部のロジックと標準入出力の処理を分離すれば、CLIツールのテストを容易に書くことができます。 今回CLIツールを作るにあたって、以下のルールを決めて実装を進めることにしました。 標準入出力、標準エラー出力、終了コードに関する処理は Command.Run 2 に実装する Command.Run 内でCLIツール固有のロジックを呼び出し、そのロジックに対してテストを書く このルールに従うことで、CLIツールの内部のロジックと標準入出力の処理を分離し、CLIツールの内部のロジックに対してテストを書くことでテストを書きやすくしています。 余談ですが、Cobraには CheckErr 3 という関数があり、エラーを渡すといい感じにエラーメッセージを標準エラー出力に出力し、終了コード 1 でコマンドの実行を終了します。 この関数を使うことで、以下のようなエラーハンドリングのコードを無くすことができます。 // before result, err := doSomething() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit( 1 ) } // after result, err := doSomething() cobra.CheckErr(err) おわりに 本記事では、CLIツールにどのような機能を実装したのか、機能を実装する際にどのようなことを考えたのかについて紹介しました。 GoでCLIツールを作るのは初めてだったのですが、Cobraの開発体験が非常に良く、作っていてとても楽しかったです。 また、Unix哲学はCLIツールを作る上で非常に参考になる考え方だと感じました。 Unix哲学に沿っている多くのCLIツールのように、skyway-cliも長く使われるツールになると良いなと思っています。 使ってみた感想や機能追加の要望、バグ報告などがありましたら、GitHubリポジトリのissueに投稿していただけると嬉しいです。 明日10日目の記事は、tetterさんのMedia over QUICに関する記事の予定です。 それでは、明日もお楽しみに!! 参考情報 UNIXという考え方 | Ohmsha Worse Is Better - 過去を知り、未来に備える。技術選定の審美眼 2019 edition / Worse Is Better - Understanding the Spiral of Technologies 2019 edition - Speaker Deck https://github.com/spf13/cobra/blob/main/site/content/projects_using_cobra.md ↩ https://pkg.go.dev/github.com/spf13/cobra@v1.8.1#Command.Run ↩ https://pkg.go.dev/github.com/spf13/cobra@v1.8.1#CheckErr ↩
アバター