TECH PLAY

電通総研

電通総研 の技術ブログ

822

こんにちは! XI本部 プロダクト イノベーション センター の佐藤です。 先日、社内のとある既存の Java プロジェクト(Gradle プロジェクト)に静的解析ツールの SpotBugs を導入する機会がありました。 『SpotBugs の Gradle プラグイン を追加して、 CI で実行すれば良いだけ。簡単かな』 と高をくくっていたら意外と工夫が必要な取り組みだったので、その知見を共有したいと思います。 なお、本記事は以下の方々を対象とした内容になります。 静的解析の基本について復習しておきたい方 冒頭にて、静的解析の概要についてあらためて説明します 既存プロジェクトに対する静的解析ツールの導入を検討されている方 本記事では SpotBugs を扱いますが、導入時の観点は他のツールにも適用可能です reviewdog の利用を検討されている方 本記事では GitHub Actions における reviewdog の利用例を掲載しています 静的解析とは 概説 実行すべきタイミング 既存のプロジェクトに対して導入する際の課題 要件を満たす運用案 reviewdog に吠えてもらう リファクタリングを推進するもう一吠え 最後に 採用リンク 静的解析とは せっかくなので、はじめに静的解析の特徴を簡単におさらいしておきましょう。 概説 静的解析とは、プログラムを実行する事なく(=静的に)隠れた潜在バグやコーディング 規約違反 を検出する解析手法です。 「プログラムを実際に動かさない」ため、静的解析の解析対象は必然的に ソースコード (あるいは バイトコード )それ自体となります。今回利用した SpotBugs も、解析対象は Java バイトコード です。プログラムの実行環境を整備する必要がない分、実行の容易性が高いと言えます。実際、SpotBugs を含め、ツールによっては IDE 上で気軽に実行可能です。 また、多くの静的解析ツールには、標準のルールセットが用意されています。したがって、例えば動的なテストにおけるテストケース設計のような手間をかける事なく、簡単にプロジェクトに導入する事ができる点も、静的解析の特徴の一つと言えるでしょう。 実行すべきタイミング もう一つ、静的解析ツールの重要な特徴として、開発者へのフィードバックの早さが挙げられます。 これは上述した「実行の容易性」とも関連しますが、コード自体を解析対象とする分、実際に手を動かしている開発者により近い場所から、迅速なフィードバックが可能です。 したがって、静的解析ツールは 開発プロセス のより早い段階で実行するのがベストプ ラク ティスと言えます。 IDE 、あるいは CI に組み込むのが良いでしょう。 今回は、SpotBugs による検出箇所の修正が確実に完了している事を担保するため、CI にて実行しています。 既存のプロジェクトに対して導入する際の課題 ここからが本題です。 既存プロジェクトに対して SpotBugs を導入する場合、「既存のコードに対する検出」をどう扱うかが課題となります。 SpotBugs が検出してくれる問題は、確かに緊急で修正すべきバグや 脆弱性 の場合もあります。 一方で、あくまでベストプ ラク ティスから逸脱しているに過ぎない、プロジェクトの状況次第では今すぐ修正すべきとまでは言えない「 リファクタリング 推奨」程度の問題である事も多いでしょう。 前者の指摘箇所は迅速に改修してしまえば良い(せざるを得ない)のですが、問題は後者です。 今回、私が SpotBugs を導入したプロジェクトは既にある程度開発が進んでおり、かつ規模が大きい事もあって、ざっと 400 件ほど「 リファクタリング 推奨」の検出箇所がありました。 これらの指摘を一気に改修するのは、 工数 的な観点から現実的ではありません。 かと言って、 CI 実行時にそれらの解析結果を無視する事を認めてしまっては、SpotBugs の運用が形骸化してしまう未来が見えてきます。 したがって 要件1:新規コードに対する SpotBugs 検出箇所の改修は必須とする(=SpotBugs の運用を形骸化させない) 要件2:既存コードに対する リファクタリング を徐々に進めていく事ができる これらの要件を満たす運用を考える必要がありました。 要件を満たす運用案 今回、上述の要件を満たすために利用したのが reviewdog というツールです。 reviewdog に吠えてもらう reviewdog は、各種解析ツールの結果を入力値として受け取り、 GitHub や GitLab といったコード ホスティング サービス上のレビューコメントとして出力してくれます。 そして、この優秀な番犬は GitHub Actions と組み合わせる事で解析ツールの実行結果を GitHub Checks のレポートとして出力する事もできるのです。 したがって、Pull Request 時の GitHub Actions による CI において SpotBugs の解析結果を reviewdog に渡す事で、当該 Pull Request の差分上に検出されたバグパターンだけを GitHub Checks で検証する事が可能となります。 なお、reviewdog はメジャーな解析ツールについては標準のパーサーがプリセットされており、 -f オプションでツール名を指定するだけで、解析結果から良い感じの指摘メッセージを生成してくれます。 残念ながら 2024年4月23日 時点で SpotBugs はサポートされていませんが、SpotBugs の出力形式として選択可能な OASIS 標準である SARIF 形式には対応済みのため、今回はこちらを利用しました。 以下、build.gradle および GitHub Actions の ワークフロー定義を掲載します。 plugins { id 'java' id( "com.github.spotbugs" ) version "6.0.12" } // ... 中略 spotbugs { // Pull Request の変更ファイル以外に検出されたバグパターンにより // CI が失敗してしまう事を回避するため、SpotBugs の失敗判定は無視 ignoreFailures = true ; } spotbugsMain { // 出力形式に sarif を指定 reports { sarif { required = true } } jvmArgs = [ '-Duser.language=ja' ] } name: reviewdog_demo on: pull_request jobs: reviewdog: name: reviewdog runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} - name: Set up Java 17 uses: actions/setup-java@v4 with: distribution: corretto java-version: 17 - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Grant permission for gradlew run: chmod +x ./gradlew - name: Run spotBugsMain run: ./gradlew spotBugsMain - name: Set up reviewdog uses: reviewdog/action-setup@v1 with: reviewdog_version: v0.17.2 - name: Run reviewdog env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: > cat ./build/reports/spotbugs/main.sarif | reviewdog -f sarif -reporter=github-pr-check -name run-reviewdog -tee reviewdog が吠えてくれたおかげで、開発者は「 リファクタリング 推奨」である既存コード上の検出は意識せず、自身が変更を加えたコード上の検出だけに集中し、CI 成功を目指すことになります。 これで、上述の要件1を満たす事ができました。 リファクタリング を推進するもう一吠え 先ほどの reviewdog による アノテーション を確認すると、SpotBugs が検出しているはずの赤枠部分のコードには アノテーション が付与されていない事が分かります。 これでは、要件1は満たせていますが、既存コード上の検出は無視され続けてしまいます。 少しずつで もリフ ァクタリングを進めていくためには、たとえ自分が書いたコードでないとしても「自身が変更を加えたファイル上に存在する既存バグパターン」くらいは一緒に修正していく事が望ましいと言えるでしょう。 そこで今回は、 reviewdog の -filter-mode という実行時オプションを利用します。 このオプションは、reviewdog がツールから受け取った解析結果を出力する際のフィルタリング方法を制御するためのフラグです。この値を file に設定する事で「追加あるいは変更されたファイル」に対する解析結果を出力させることができます。 name: reviewdog_demo on: pull_request jobs: reviewdog: name: reviewdog runs-on: ubuntu-latest steps: #... 中略 - name: Run reviewdog env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # -filter-mode file を指定 run: > cat ./build/reports/spotbugs/main.sarif | reviewdog -f sarif -reporter=github-pr-check -name run-reviewdog -filter-mode file -tee これにより、Pull Request の変更ファイル上のバグパターンはすべて CI で検出される事となり、要件2も良い感じに満たす事ができました。 細かい要望にも答えてくれる reviewdog、ハチ公もびっくりの忠犬ですね 🐶 最後に 本記事では、既存プロジェクトに対する SpotBugs の導入を、reviewdog + GitHub Actions により着実に進めていくノウハウを紹介しました。 「既存コードに対する検出をどう扱うか」という問題は、SpotBugs に限った話ではないと考えています。 本記事のアプローチが、既存プロジェクトに対する解析ツール運用を検討している方々の一助となれば幸いです。 採用リンク 私たちは一緒に働いてくれる仲間を募集しています! 次世代会計プロダクトDevOpsエンジニア(Ci*Xシリーズ) 執筆: @satorin 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 Web サービスへの攻撃を防ぐために WAF を使いましょうというのはよく聞きます。 ではインターネットに公開した Web サービスに送信される悪意のあるリク エス トがどれぐらい WAF によって防御され得るのでしょうか? お手軽に使える AWS WAF のマネージドルールを対象に確かめてみました。 この記事の概要 実験用に固定レスポンスを返すだけの HTTP エンドポイントを作成し、約半年間インターネットからアクセス可能な状態で放置した AWS WAF のマネージドルールをアタッチしており、それぞれのルールに一致したリク エス ト数を集計し、ランキング形式にまとめた 一致したリク エス ト数が多いマネージドルールそれぞれに対して、どのようなリク エス トが多かったのか集計し、ランキング形式にまとめた 実験用システムの構成 基本的なデータの集計 ログ総数 国名コード別のリクエスト数 HTTPメソッド別のリクエスト数 それぞれのマネージドルールグループに一致したリクエスト数 一致した数が多い個別のマネージドルール 1位: AWSManagedRulesAnonymousIpList - HostingProviderIPList 2位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH 3位: AWSManagedRulesAdminProtectionRuleSet - AdminProtection_URIPATH 4位: AWSManagedRulesCommonRuleSet - NoUserAgent_HEADER 5位: AWSManagedRulesLinuxRuleSet - LFI_URIPATH 6位: AWSManagedRulesCommonRuleSet - UserAgent_BadBots_HEADER 7位: AWSManagedRulesAmazonIpReputationList - AWSManagedReconnaissanceList 8位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH_RC_COUNT 9位: AWSManagedRulesCommonRuleSet - RestrictedExtensions_URIPATH 10位: AWSManagedRulesAmazonIpReputationList - AWSManagedIPReputationList 11位: AWSManagedRulesCommonRuleSet - GenericLFI_URIPATH まとめ 実験用システムの構成 ALB を主体としたシンプルな構成としました。 AWS 東京リージョンに200番の固定レスポンスを返すだけの ALB を構築した ALB のセキュリティグループは 80 ポートと 443 ポートのみをオープンにし、 0.0.0.0/0 からのアクセスを許可した 独自ドメイン の ACM 証明書を取得し、ALB にアタッチした。ホストゾーンのレコードも ALB を向くように構成した ただし ALB の IP アドレスも、 独自ドメイン 名も特に外部向けに公表していない WAF Web ACL をアタッチし、 AWS マネージドルールグループを複数適用した ただしマネージドルールグループのアクションは全て COUNT にオーバーライドし、ルールに一致してもリク エス トがブロックされないようにした 適用したマネージドルールグループは次の11個である AWSManagedRulesCommonRuleSet AWSManagedRulesAdminProtectionRuleSet AWSManagedRulesKnownBadInputsRuleSet AWSManagedRulesSQLiRuleSet AWSManagedRulesLinuxRuleSet AWSManagedRulesUnixRuleSet AWSManagedRulesWindowsRuleSet AWSManagedRulesPHPRuleSet AWSManagedRulesWordPressRuleSet AWSManagedRulesAmazonIpReputationList AWSManagedRulesAnonymousIpList 全てデフォルトバージョンを適用した。期間中にバージョンがアップデートされている場合がある WAF のログを S3 バケット に出力するよう構成した 補足として、ルールのアクションは全て COUNT にオーバーライドしても、特定のルールに一致した場合はリク エス トに特定のラベルが付与されます。ログに出力された各リク エス トのラベルを確認することで、どのルールに一致したかを判別できるようになっています。アクションは全て COUNT なので、複数のルールに一致した場合には、一致した全てのルールのラベルが付与されます。 2023年10月中旬に稼働を開始し、2024年4月上旬までの約173日間のログを対象に集計を行いました。 基本的なデータの集計 ログ総数 稼働期間中のログ総数と、マネージドルールに一致したログエントリー数をまとめました。 説明 件数 総数に対する割合 ログエントリー総数 153,220 件 100% いずれかのマネージドルールに一致したログエントリー数 115,844 件 76% いずれのマネージドルールにも一致しなかったログエントリー数 37,376 件 24% 記録されていたログエントリー数(リク エス ト数)は153,220件で、稼働期間の173日で割ると約886件/日となります。何も外部に公表していない HTTP エンドポイントでもこれだけのリク エス トがインターネットから届いていることがわかります。 いずれかのマネージドルールに一致したログエントリー数は 115,844件で、全体の約76%にあたります。これだけの割合のリク エス トが、今回利用した AWS WAF マネージドルールのブロック対象となっていたということです。 国名コード別のリク エス ト数 マネージドルールに一致したかどうかに関わらず、全てのリク エス トについて国名コード別の上位10件をまとめました。 順位 国名コード 件数 総数に対する割合 1 US 47,129 件 31 % 2 IL 17,172 件 11 % 3 FR 14,883 件 10 % 4 GB 12,890 件 8 % 5 SG 7,189 件 5 % 6 JP 7,026 件 5 % 7 DE 5,972 件 4 % 8 ID 5,759 件 4 % 9 NL 5,408 件 4 % 10 CN 5,219 件 3 % HTTPメソッド別のリク エス ト数 マネージドルールに一致したかどうかに関わらず、全てのリク エス トについてHTTPメソッド別のリク エス ト数をまとめました。 順位 HTTPメソッド 件数 総数に対する割合 1 GET 129,700 件 85 % 2 POST 19,839 件 13 % 3 HEAD 3,113 件 2 % 4 OPTIONS 565 件 0 % 5 PURGE 2 件 0 % 6 PUT 1 件 0 % GET リク エス トが圧倒的に多く、全体の 85% を占めています。 それぞれのマネージドルールグループに一致したリク エス ト数 適用した11個のマネージドルールグループそれぞれに一致したリク エス ト数と割合をまとめました。注意点として複数のルールに一致した場合には、一致したルール全てに重複して計上されています。 マネージドルールグループ 件数 リク エス ト総数に対する割合 AWSManagedRulesCommonRuleSet 18,286 件 12 % AWSManagedRulesAdminProtectionRuleSet 10,069 件 7 % AWSManagedRulesKnownBadInputsRuleSet 16,081 件 10 % AWSManagedRulesSQLiRuleSet 6 件 0 % AWSManagedRulesLinuxRuleSet 8130 件 5 % AWSManagedRulesUnixRuleSet 23 件 0 % AWSManagedRulesWindowsRuleSet 111 件 0 % AWSManagedRulesPHPRuleSet 243 件 0 % AWSManagedRulesWordPressRuleSet 226 件 0 % AWSManagedRulesAmazonIpReputationList 6,289 件 4 % AWSManagedRulesAnonymousIpList 100,886 件 66 % 大きな差はありますが、いずれのルールグループも一致するリク エス トがありました。目立って多いのは AWSManagedRulesAnonymousIpList ルールグループで、全リク エス トの 66% が一致しました。 一致した数が多い個別のマネージドルール マネージドルールグループの中でも、一致したリク エス ト数が多い個別のマネージドルールを集計しました。 1000件以上のリク エス トが記録された上位11種類を掲載します。 順位 マネージドルールグループ 個別ルール名 件数 リク エス ト総数に対する割合 1 AWSManagedRulesAnonymousIpList HostingProviderIPList 100,706 件 66 % 2 AWSManagedRulesKnownBadInputsRuleSet ExploitablePaths_URIPATH 12,499 件 8 % 3 AWSManagedRulesAdminProtectionRuleSet AdminProtection_URIPATH 10,069 件 7 % 4 AWSManagedRulesCommonRuleSet NoUserAgent_HEADER 8,154 件 5 % 5 AWSManagedRulesLinuxRuleSet LFI_URIPATH 7,654 件 5 % 6 AWSManagedRulesCommonRuleSet UserAgent_BadBots_HEADER 4,958 件 3 % 7 AWSManagedRulesAmazonIpReputationList AWSManagedReconnaissanceList 4,728 件 3 % 8 AWSManagedRulesKnownBadInputsRuleSet ExploitablePaths_URIPATH_RC_COUNT 3,525 件 2 % 9 AWSManagedRulesCommonRuleSet RestrictedExtensions_URIPATH 2,774 件 2 % 10 AWSManagedRulesAmazonIpReputationList AWSManagedIPReputationList 1,493 件 1 % 11 AWSManagedRulesCommonRuleSet GenericLFI_URIPATH 1,363 件 1 % 最多は AWSManagedRulesAnonymousIpList ルールグループの HostingProviderIPList ルールで、全体の 66%を占めています。 2位以下は目立って多いルールはなく、ルールグループも分散している印象です。 続いてそれぞれのルールについて多かったリク エス トをもう少し詳しく集計してみました。 1位: AWSManagedRulesAnonymousIpList - HostingProviderIPList 公式ドキュメント の説明: エンドユーザー トラフィック のソースになる可能性が低いウェブ ホスティング プロバイダーと クラウド プロバイダーの IP アドレスのリストを検査します。IP リストには AWS IP アドレスは含まれません。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 US 28,305 件 28 % 2 IL 17,165 件 17 % 3 FR 14,043 件 14 % 4 GB 9,265 件 9 % 5 JP 6,020 件 6 % 2位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH 公式ドキュメント の説明: URI パスに、悪用可能な ウェブアプリケーション パスにアクセスする試みがないかを検査します。パターンの例には、web-inf などのパスがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /.env 3,328 件 2 /. aws /config 154 件 3 / api /.env 96 件 4 / aws /credentials 94 件 5 /laravel/.env 77 件 6 /[具体的な IPアドレス ]/.env 72 件 7 /.env.prod 68 件 8 /app/.env 63 件 8 /.env.production 63 件 8 /docker/.env 63 件 機密情報が記録されている可能性がある .env 関係のファイル、 AWS の configや機密情報が格納されているファイルを取得しようとしているリク エス トが上位を占めました。 6位は具体的な IPアドレス が記録されており、リク エス トが行われた当時のALBに割り当てられた IPアドレス だと思われます。 3位: AWSManagedRulesAdminProtectionRuleSet - AdminProtection_URIPATH 公式ドキュメント の説明: 一般的にウェブサーバーまたはアプリケーションの管理用に確保されている URI パスの有無を検査します。パターンの例には、sqlmanager などがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /admin-app/.env 281 件 2 /admin/phpinfo. php 210 件 3 /boaform/admin/formLogin 182 件 4 /admin. php 172 件 5 /admin/.env 141 件 6 /linusadmin-phpinfo. php 97 件 7 /admin/index.html 88 件 8 / phpmyadmin /index. php 77 件 9 /admin/info. php 74 件 10 /admin/config. php 73 件 さまざまな admin を含むパスへのリク エス トが一致していることがわかります。 4位: AWSManagedRulesCommonRuleSet - NoUserAgent_HEADER 公式ドキュメント の説明: HTTP User-Agent ヘッダーが欠落しているリク エス トを検査します。 User-Agent ヘッダーがないリク エス トなので、別観点での集計は割愛します。 5位: AWSManagedRulesLinuxRuleSet - LFI_URIPATH 公式ドキュメント の説明: リク エス トパスに、 ウェブアプリケーション のローカルファイル インクルージョン (LFI) の 脆弱性 を悪用する試みがないかを検査します。パターンの例には、攻撃者に オペレーティングシステム の情報を提供できる /proc/version などのファイルがあります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった10件をまとめました。 順位 リク エス トURLパス 件数 1 /wp-config. php .bak 102 件 1 /. vscode /sftp. json 102 件 3 /wp-config. php .backup 76 件 3 /wp-config.txt 76 件 3 /wp-config.old 76 件 6 /admin/config. php 73 件 7 /app/config/parameters.yml 61 件 8 / phpMyAdmin /scripts/setup. php 60 件 9 /wp-config. php -backup 59 件 10 /wp-config. php .old 53 件 1位の /wp-config.php.bak は WordPress の設定ファイルのバックアップを取得しようとするリク エス トだと思われます。 2位の /.vscode/sftp.json は FTP や SFTP でファイル転送を行う VSCode の拡張の設定ファイルで、パスワード情報が記録されている可能性のあるものです。 全体として設定情報や機密情報の窃取を目的としているリク エス トが多く一致しているようです。 6位: AWSManagedRulesCommonRuleSet - UserAgent_BadBots_HEADER 公式ドキュメント の説明: リク エス トが不正なボットであることを示す一般的な User-Agent ヘッダー値を検査します。パターンの例には、nessus、nmap などがあります。 このルールに一致したリク エス トのうち、 User-Agent ヘッダー別にリク エス ト数が多かった順にまとめました。 一部の User-Agent ヘッダーには URL リンクが付与されていましたが、改変し削除しました。 順位 User-Agent ヘッダー 件数 1 Mozilla /5.0 zgrab/0.x 3,207 件 2 Mozilla /5.0 (compatible; Nmap Scripting Engine;) 1,519 件 3 Fuzz Faster U Fool v2.1.0-dev 70 件 4 masscan/1.3 65 件 5 ivre-masscan/1.3 30 件 5 masscan/1.0 30 件 7 Fuzz Faster U Fool v2.0.0-dev 21 件 8 masscan-ng/1.3 14 件 1位の Mozilla/5.0 zgrab/0.x は 脆弱性 スキャンツールによるリク エス トに付与される User-Agent ヘッダー値のようです。 他は主にポートスキャンツールによるリク エス トに付与されるヘッダー値のようです。 7位: AWSManagedRulesAmazonIpReputationList - AWSManagedReconnaissanceList 公式ドキュメント の説明: AWS リソースに対して偵察を実行している IP アドレスからの接続を検査します。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 FR 2,853 件 60 % 2 US 578 件 12 % 3 BR 363 件 8 % 4 DE 294 件 6 % 5 HK 216 件 5 % 8位: AWSManagedRulesKnownBadInputsRuleSet - ExploitablePaths_URIPATH_RC_COUNT 2位と同じ現象だと思われるので割愛します。 AWS WAFでは新しいマネージドルールのリリース候補に対し、テストの期間を設けて RC_COUNT を名前に付加して稼働しているそうです。 9位: AWSManagedRulesCommonRuleSet - RestrictedExtensions_URIPATH 公式ドキュメント の説明: 読み取りや実行が安全でないシステムファイルの拡張子が URI パスに含まれているリク エス トを検査します。パターンの例には、.log や .ini などの拡張子があります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった12件をまとめました。 順位 リク エス トURLパス 件数 1 /.env.bak 148 件 2 /wp-config. php .bak 102 件 3 / php .ini 88 件 4 /wp-config. php .backup 76 件 5 /.env.backup 69 件 6 /.config 48 件 7 /env.backup 34 件 8 /wp-config.bak 32 件 9 /wp-config.backup 30 件 9 /wp-config.conf 30 件 9 /wp-config-sample. php .bak 30 件 9 /wp-config.cfg 30 件 他のルールにも一致しているリク エス トパスも含まれていました。 .env 関係のファイルや WordPress 関係のファイルの取得が多く一致している印象です。 10位: AWSManagedRulesAmazonIpReputationList - AWSManagedIPReputationList 公式ドキュメント の説明: ボットとして識別された IP アドレスを検査します。 このルールに一致したリク エス トのうち、国名コード別にリク エス ト数が多かった5件をまとめました。「割合」列はこのルールに一致したリク エス トに占める割合を示しています。 順位 国名コード 件数 割合 1 ID 878 件 59 % 2 US 144 件 10 % 3 FR 125 件 8 % 4 GB 98 件 7 % 5 AU 62 件 4 % 11位: AWSManagedRulesCommonRuleSet - GenericLFI_URIPATH 公式ドキュメント の説明: URI パスに、ローカルファイル インクルージョン (LFI) を悪用する形跡がないかを検査します。例には、../../ などの手法を使用した パストラバーサル の試行があります。 このルールに一致したリク エス トのうち、リク エス トURLパス別にリク エス ト数が多かった順にまとめました。 順位 リク エス トURLパス 件数 1 /. aws /credentials 1287 件 2 /. aws /credentials.js 19 件 2 /root/. aws /credentials 19 件 2 /home/. aws /credentials 19 件 5 / api /v1/totp/user-backup-code/../../system/system-information 11 件 6 /rjdi../meme.1 4 件 7 /static../.git/config 3 件 8 /media../.git/config 1 件 AWS の機密情報が記録されているファイルを窃取しようとするリク エス トが多く一致していました。 まとめ どれぐらいの悪意があったのかは判断しませんが、全リク エス トの76%が適用したいずれかの WAF マネージドルールに一致していました。 集計の上位にあたるリク エス トは明確に機密情報ファイルの窃取を狙っているものが多数あり、 AWS WAF のマネージドルールを利用するだけでもそのうちのかなりの割合をブロックできると考えると、インターネットからアクセスできるWebエンドポイントを公開する場合には ( AWS WAFに限らず) WAF の適用をぜひ検討したほうが良いでしょう。 AWS WAFについては、過去にもいくつか記事を執筆しているのでご参照いただけますと幸いです。 AWS WAFがリクエストをブロックした時に、自動的にIPアドレスレベルで一定期間ブロックし続ける方法 AWS WAF について最初から知りたかったこと8選 AWS WAF マネージドルールグループの個別ルールに条件を追加する方法 私たちは一緒に働いてくれる仲間を募集しています! キャリア採用 クラウドアーキテクト 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター
こんにちは。CIT事業部の石際です。 LINE周辺のシステム構築やカスタマーサポート領域のシステム導入などを担当しています。 LINEをカスタマーサポートのチャネルとして利用している企業は多いですが、まだまだ電話問い合わせのチャネルとして利用しているケースは少ないです。 そこで今回は、LINEからコンタクトセンターへ電話ができる「LINEコールPlus」というサービスについて、 LINEコールPlusの 利用 ユースケース や 顧客体験 などもあわせてご紹介します! 1. LINEコールPlusとは LINEコールPlusの仕組み 2. システム構成のパターン ① 複数の既存の問い合わせ窓口に転送 ② 複数の電話番号にルーティング ③ ソフトフォンで電話応対 ④ コールセンターシステムとの連携 3. LINEコールPlusを利用して顧客体験を向上させる 発信用URLに追加できるパラメーター ビジュアルIVR 4. まとめ 1. LINEコールPlusとは 「LINEコールPlus」は、LINE公式アカウントの 有料オプション 機能で、ユーザーがLINE公式アカウントから無料でカスタマーサポートに電話できるサービスです。 LINEコールPlusの仕組み LINEとPBXなどの音声通信基盤を接続することで、LINEアプリからコンタクトセンターへの着信が可能になります。 LINEから発行された発信用URLにユーザーがアクセスすることで通話を開始します。 発信時には、LINE公式アカウントの友だち追加(ブロック解除)が確認され、通話が開始されます。 音声通信基盤は各社さまざまな製品・サービスがありますが、これまでの経験から、導入コストが低く拡張性に富んだTwilioを採用しています。 コンタクトセンタープラットフォーム「Twilio Flex」 | ソリューション | 電通総研 これから説明する ユースケース は全てTwilioの利用を想定しています。 2. システム構成のパターン 「ただLINEと電話を繋ぐだけでしょう?」と思うかもしれませんが、それぞれの企業により、お問い合わせ窓口の体制や運用状況は異なります。 各々の要件を満たすためには、単純に繋ぐだけでなく音声通信基盤側のカスタマイズが欠かせません。 具体的にどのようなシステム構成、カスタマイズパターンがあるか、要件に応じて解説します。 ① 複数の既存の問い合わせ窓口に転送 LINEコール(無料機能)では、1つの電話番号のみに転送が可能でした。 しかし、LINEコールPlusを利用すれば、複数の電話番号に転送できます。 IVR(Interactive Voice Response:自動音声応答システム)で要件を確認すれば、複数の窓口があってもそれぞれに転送できます。 既存のコンタクトセンターを大きく変えることなく、新たなチャネルとしてLINEを追加できるのが大きなメリットです。 ② 複数の電話番号にルーティング TwilioのACD(Automatic Call Distribution:着信呼自動分配装置)機能を使って、複数の電話番号にルーティングします。 最初の電話番号と通話ができなければ、次の電話番号にコールするなどコールフローの設計が可能です これは、コールフローを新たに作るので、LINEを中心とした問い合わせ窓口を新設する場合などに有効です。 ③ ソフトフォンで電話応対 電話番号への転送ではなく、オペレーターがソフトフォン(Twilio Flex )で受電し、通話します。 ※ Twilio Flex とは? クラウド 型コンタクトセンタープラットフォーム。 電話、チャット、SMSなどマルチチャネルに対応し、オペレーターの画面のUIやIVR、ACDなどのフルカスタマイズが可能です。 TwilioのACD機能を使って、オペレーターに電話を割り当てます。 オペレーターは Flex の画面から着信を確認し、通話します。 Flex は クラウド サービスのため、オペレーターは 在宅ワーク など場所を問わず働くことが可能です。 また、電話番号はTwilioで取得するため、電話回線を準備する必要もなく、コストを抑えられるメリットもあります。 ④ コールセンターシステムとの連携 Twilio Flex は、コンタクトセンターシステムと CTI (Computer Telephony Integration)連携することも可能です。 他のチャネルでのカスタマーサポートと同時に、LINEからのお問い合わせも管理できます。 このように柔軟な設計が可能なのが、LINEコールPlusとTwilioの最大の魅力です。 規模やLINEというチャネルの位置づけに応じた設計が重要となります。 3. LINEコールPlusを利用して顧客体験を向上させる 次に、ユーザーの視点からどのような価値を提供できるかを考えてみましょう。 もちろん、ユーザーにとって最大のメリットは、馴染みのあるLINEから無料で電話ができることです。 その他にLINEコールPlusを利用した場合、どのような顧客体験を提供できるでしょうか。 以下に少し発展的な方法をご紹介します。 発信用URLに追加できるパラメーター LINEコールPlusでは、ユーザーが発信用URLをクリックすることで通話を開始します。 この発信用URLには「パラメーター」を追加できます。 パラメーターを利用することで、カスタマーサポートをよりシームレスかつ効率的に行うことができます。 ビジュアルIVR IVRの音声ガイダンスを聞いてボタンを押すのは面倒なことがあります。 しかし、チャットまたはWeb画面から要件を選択し、その選択を発信用URLの「パラメーター」に入れて渡すことで要件に応じた電話のルーティングが可能になります。 これにより、ユーザーはIVRによるストレスから解放されます。 4. まとめ いかがでしたか。 LINEコールPlusの利用は、事業者にもユーザーにも多くのメリットがあります。 個人的には、 海外の日本人向けの問い合わせ窓口 LINE利用者層を対象とした問い合わせ窓口 などが非常に有効だと思います。 チャネルが充実しコンタクトセンター内の電話の件数が減っていると言われている今でも、一定の需要は存在します。 そして、電話、チャット、Webアプリなどが使えるLINEは、カスタマーサポートの領域で顧客体験(CX)の向上に貢献してくれるでしょう。 弊社のLINEコールPlusのソリューションページには導入時に必要な手続きなども記載していますので よろしければ合わせてご覧ください。 www.dentsusoken.com 最後までお読みいただき、ありがとうございました。 私たちは同じ事業部で共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! <電通×IT>電通グループ基幹システムプロジェクトマネージャー エンタープライズ向けDX推進リーダー/エンジニア <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 製品・プラットフォーム開発エンジニア 執筆: @ishigiwa_yumi 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは。 電通 総研X イノベーション 本部プロダクト イノベーション センターの中山です。好きなものは自動化、嫌いなものは属人化です。 この記事では、私の所属するチームがとある課題を解決するために開発したアプリケーションについて、課題の発見からアプリケーションの内容、開発にあたって重要視した価値観について掲載します。 背景(IaCの素晴らしさについて) 電通 総研では様々なパッケージ製品を展開し、それらを様々な業界のお客様にお使い頂いています。 特に大企業のお客様にご利用頂く場合、パッケージ製品の標準機能にお客様専用のアドオンを付与し、インフラについても専用の環境を求められるケースが多くあります。 有難いことに数多くのお客様に製品をご利用頂いている 電通 総研としては、お客様ごとに専用となるインフラ環境を構築し、管理する必要が生じるわけです。 インフラを構築する方法として、最も原始的なのは、手順書を作成し、それに倣って構築を実施する方法です。 この方法は特別なスキルを必要とせず、チームへの導入が容易で、比較的属人化が発生しにくいという利点があります。一方で、人間が手順書を見ながら慎重に作業を行う必要がありますので、時間や 工数 などのコスト効率に課題があります。また、人間は必ずミスを犯してしまうという前提に立つと、作業ミスのリスクもあり、作業品質面にも課題があります。 この問題を解決するために、インフラをコード化して管理するInfrastructure as code(IaC)が近年広く使われるようになっています。代表的な技術として、 Terraform 等があります。 IaCは個人的にとても大好きな技術です。従来の手順書を用いてインフラを構築していた時代には、どのような作業を実施し、どのようなインフラを構築したか、といった作業履歴を残すのは大変な作業でした。ドキュメントの記述の粒度やルールを統一することには限界があり、作業者の性質にドキュメントの品質が左右され、運用が長期化するほどに、ドキュメントの信憑性が低下してしまうことは避けられない課題でした。 IaCの登場により、その状況は一変します。コードに厳密に定義した内容を入力として、インフラを出力するIaCにおいては、最新のコードの内容が、最新の環境を表す証拠そのものになります。コードはGit等のバージョン管理ツールを用いて管理することが可能なので、コードの変更履歴、すなわち、インフラの変更履歴を正確に管理できます。人間と違って、コードは嘘をつけません。このドキュメントは本当にちゃんと正確に更新され続けているのだろうか...等と悩む必要はなくなったのです。 属人化という課題 電通 総研でも、様々なプロジェクトにIaCが導入されています。その中で、一つの課題が見えてきました。 IaCは非常に強力なツールですが、うまく標準化できないと、その資産が構築した技術者に依存してしまうということが発生します。例えば、「新規にインフラ環境を追加する」、「既存のインフラ環境の設定を少し更新する」、といった様々な作業が、下手をすると一人の技術者に依存し、その人でなければ作業が実施できない、といった状況へと陥ります。 世はまさに、超人手不足時代。業績を伸ばし続けている 電通 総研にとってもそれは例外ではなく、増え続ける案件の数に対して、IaCに対応できる技術者の数は限られています。そのようなある意味貴重な技術者に、運用で発生するあらゆる作業が依存されることは、本人にとっても周囲にとっても不幸なことです。プロジェクトにとってもリスクとなり得る、避けるべき事態です。 解決に向けて そこで我々は、「新規にインフラ環境を追加する」、「既存のインフラ環境の設定を更新する」といった運用で頻繁に発生し得る作業を、シンプルなアプリケーション操作で実行可能となるよう標準化することで、この問題の解決へと乗り出しました。 頻発する運用作業がシンプルな GUI 操作で実行可能になることで、属人化の問題を防止することに加え、このアプリケーションを導入することで、抽象化されて用意されている手順に従うだけで、高効率・高品質な作業プロセスが実現可能となるというところに、大きな価値があります。 加えて、これまでは別で管理していた管理運用に関わる情報を、こちらのアプリケーションに集約することも可能になります。 開発方針 このアプリケーションの開発に際して最も重視したのは、「まずは PMF (Product Market Fit)を確かめることに注力する」ということです。時間をかけて機能を作り込んでも、それらがユーザーにとって価値がなく、まったく使ってもらえないものとなってしまったら意味がありません。そこで我々は、「低コストかつ短期間で必要最低限の機能を備えた製品を開発し、実際にユーザーに使ってもらい検証し、学びを得ながら製品を磨いていく」という リーンスタートアップ の考え方を取り入れてプロダクトを開発しています。ユーザーの反応を的確に取得して、より満足されるサービスに仕立てていくということが、何よりも重要なことです。 まとめ 非常に簡単にではありますが、我々のアプリケーション開発についてご紹介しました。 開発方針としてもご紹介した通り、我々の製品はユーザーの声を大切にしながら、日々進化を続けています。完成するアプリケーションも、それを支えるプロセスも、日々変化していくことを楽しみながら、これからも開発を継続していきたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! フルサイクルエンジニア 執筆: @nakayama.seiya2 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
みなさん、こんにちは。 電通 総研 金融事業部の鈴木です。 最近業務で AWS を触る機会がなくなり、復習したいと考えていたところ「 AWS Cloud Quest 」というサービスを見つけました。 2024年3月にSolution Architect用のコンテンツが日本語対応したという AWSブログ の記事を読み、存在を知りました。 今回はこの「 AWS Cloud Quest」を利用して AWS の基本サービスを復習してみて、良かった点・気になった点やどんな人にオススメかを紹介します。 AWS Cloud Quest とは? 利用方法 実際に試してみた 1.学習 2.計画 3.実践 4.DIY 完了報告 良かった点 気になった点 どんな人にオススメか? まとめ AWS Cloud Quest とは? AWS Cloud Questとは、実用的な AWS Cloud スキルを身につけるための唯一の3D ロールプレイングゲーム です。( AWS より引用) つまり、ゲームを通して AWS の実用的な知識と経験を積んでいくための学習サービスです。 学習者はロールと呼ばれる学習レベル・分野を選択し、 クラウド スキルを身につけながら仮想都市の市民の困りごとを解決します。 ロールには、Cloud Practitioner、Solution Architect、サーバーレス デベロッパ ー、 機械学習 スペシャ リスト、セキュリティ スペシャ リストなどがあります。 現在日本語に対応しているコンテンツは、Cloud Practitioner、Solution Architectの2つです。 また、Cloud Practitionerのみ無料で学習できます。 今回は、このCloud Practitionerを利用して学習します。 利用方法 AWS Cloud Questを利用するために、まずは AWS Skill Builder のサイトにアクセスします。 フィルターから「Training Category > Game-Base Learning」にチェックを入れ、表示された「 AWS Cloud Quest: Cloud Practitioner (Japanese) 日本語版」をクリックします。 表示されたサービスが「 AWS Cloud Quest: Cloud Practitioner (Japanese) 日本語版」で「Free」であることを確認し、「ENROLL」をクリックします。 するとサインインが求められるため、「 SIGN IN AWS SKILL BUILDER」をクリックして、サインイン画面に進みます。 AWS Cloud Questの利用には、無料のアカウント登録が必要のためアカウントを登録します。APN( AWS Partner Central)アカウントをお持ちの方はAPNアカウントでもログイン可能です。 ログイン後、画面中央に表示されるコンテンツをスクロールし、「ゲームを開始する!」をクリックしてください。 別タブでゲーム画面が開きます。 初期の言語設定が英語になっているため、以下画像のように「歯車マーク→言語→日本語」を選択して、言語を日本語に設定してください。 (日本語版を選択したのにデフォルトは英語なんですよね…) 言語設定ができましたら、トップ画面の「ゲーム開始」をクリックしてゲームを開始してください。 実際に試してみた ゲームを開始すると、ストーリーの説明が入ります。 AWS を活用して街の課題を解決していくのが目的のようです。 しばらく進んでいくと、 アバター の作成に移ります。身体、顔、服装などが設定できます。自分の好みで作成してみてください。 そのまま進めると、困りごとを抱えた人に出会います。これらの課題を ヒアリ ングし、 AWS を用いてどのように課題解決するかを考えていきます。 以下の画像ではイン フラリ ソースのプロビジョニングに関する課題を抱えており、 Amazon S3 を利用して静的Webサイトをデプロイすることで解決しようとします。 AWS Cloud Questでは、上記のような依頼者とのやり取りの後、「1.学習」「2.計画」「3.実践」「4. DIY 」の順で学習を進めていきます。 1.学習 まずは学習です。今回構成する アーキテクチャ の概要や、サービスについて学習します。 それぞれ英語ではありますが解説動画がついており、「3.実践」で実際にコンソールを触る前に、課題で利用するサービスの概要やベストプ ラク ティスを学習できます。 2.計画 次に計画です。今回構築する アーキテクチャ について全体像を理解し、何をすべきかを把握します。 この アーキテクチャ 構成や、やるべき操作を事前に理解しておくことで学習の理解度が上がります。 3.実践 ここまで来ましたら、実際に AWS のコンソールを操作してシステムを構築します。 左上の「 AWS コンソールを開く」をクリックすることで、課題用の環境が AWS 側でプロビジョニングされてコンソール画面から利用できます。 学習用に別途 AWS アカウントを用意する必要がなくてとても快適です。 これによってプロビジョニングされる環境では、課題に関係のないサービスにはアクセスできないようになっているとのことです。 しかし、事故を防ぐためにも画像赤枠のステップにある手順や、課題解決に関係ある操作から逸脱したことはしないように心がけましょう。 画像赤枠のステップに沿って操作をしていくと、課題解決に必要な知識・操作を身につけることができます。 ステップを進めていくと、コンソール画面の操作について説明や解説を表示してくれます。 4. DIY 最後に DIY です。「3.実践」のコンソール画面から課題を達成します。 DIY では「3.実践」のようなステップはありませんが、これまでの知識を用いてシステムを構築していきましょう。 左上の「 AWS コンソールを開く」をクリックしてコンソール画面を開きます。 課題をクリアするために、以下画像の赤点線枠を参考に AWS リソースの設定をしてシステムを構築します。 システムの構築が完了したら検証します。 指定された内容を入力して、「検証」をクリックすることで正しく設定できているかを確認してもらえます。 (以下の画像は設定が間違っていたため検証エラーになっており、何がよくなかったのかを教えてくれています) 正しく設定できた場合、「お疲れ様でした」のメッセージが表示されます。 正しくシステム構築できたため、依頼主に報告して完了という流れになります。 完了報告 課題を解決したら依頼主に報告します。 お礼に住んでいる街を発展させることができます。結構テンション上がりますね。 以上のような流れで、困りごとを抱えた人の課題を AWS で解決していくことで AWS の知識と実践的な経験を積むことができます。 良かった点 AWS Cloud Questの良かった点をまとめていきます。 総じて良い学習体験を得られましたが、特に以下の点が良いと思いました。 ①課題ごとに学習環境を提供してくれるため、自分で学習環境をセットアップする手間を省ける。 「実際に試してみた」の章で紹介した通り、 AWS Cloud Questではボタンひとつで学習環境がプロビジョニングされて利用できます。 別途 AWS アカウントを用意せずとも、ワンクリックで10秒程度で環境が提供されるため、学習ハードルはとても下がります。 通常 AWS の学習をする場合、 AWS アカウントを作成して無料枠を気にしながら学習する場合が多いのではないでしょうか。 この環境は、課題が終了するとアクセス権がなくなり、自動的に破棄されます。 そのため、リソースの削除し忘れによる課金を心配することもありません。 (EC2などの止め忘れとかでエライ目を見る心配がなくて良いですね) ②コンソール画面の操作解説が丁寧で、実際にコンソール画面を操作しながら学習できる。 「実際に試してみた」の章で紹介した通り、課題をクリアするために必要な操作は丁寧に解説されます。 特に初学者に対して、ハンズオン形式でコンソール画面を操作できるため、安心して操作できると思います。 ③ AWS リソースを設定後、設定内容が正しいかを AWS 側で簡単に検証してくれる。 「実際に試してみた」の章で紹介した通り、設定したリソースの設定内容が正しいかどうかを AWS 側で検証してくれます。 紹介した例では、S3の バケット 名を入力することで検証されます。 学習用に自分で環境構築して AWS リソースを設定した場合、意図した設定になっているかを検証することは、時として大変なことがあるかと思います。 どのように検証するかを自分で考えることに学びがある場合は多々ありますが、検証を簡単にできることは学習ハードルを下げる面でメリットだと思います。 気になった点 AWS Cloud Questの気になった点は、特にありませんでした。 強いていうなら主人公の アバター が少し怖いところでしょうか…? どんな人にオススメか? AWS Cloud Questは以下のような人たちにオススメしたいです。 AWS について勉強したいが、どこから勉強して良いかわからない人 コンソール画面を実際に触って学習したい人 今回はCloud Practitionerを触ったということもありますが、 AWS 初学者には入門教材としてとても良いのではないかと思います。 AWS 環境を自前で用意する必要がなく、Cloud Practitionerについては無料でコンソールを操作しながら学習できます。そのため、「従業員に手を動かしながら AWS を学習してほしいが、 AWS 環境を用意するのが難しい」という課題にもマッチしているのではないでしょうか? まとめ 今回は AWS Cloud QuestのCloud Practitionerを利用して、 AWS の基本サービスを復習してみました。 ゲーム形式と言いつつ、 AWS のコンソール画面を操作しながら学習できるため学びが多いと感じました。 「良かった点」の章でも紹介した通り、 AWS の学習ハードルを下げつつ、コンソール画面を操作しながら学べるというのが強みだと思います。 もちろん AWS Cloud Questの範囲から外れる内容がある場合や自由に AWS サービスを触りたい場合は、自前の AWS アカウントで学習するのが良いと思いますが、 AWS を学習する上で選択肢の一つには入れても良いのではないかと思いました。 機会があれば、Cloud Practitioner以外も試してみたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! 【金融×IT】システム アプリケーション・アーキテクト 【金融×IT】クラウドアーキテクト ◎技術力を備えたリーダを目指せる フルサイクルエンジニア 執筆: @suzuki.takuma 、レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
こんにちは、 電通 総研、X イノベーション 本部 AIトランスフォーメーションセンター所属の徳原光です。 普段は、主に Python でPandasを使ってデータ分析やAI学習・推論のための特徴量を生成を行っていますが、最近KotlinでKotlin DataFrameを操作してデータ処理を実装する機会が増えました。 これまでローカル上で開発してきた 機械学習 モデルをONNXを使ってアンドロイド スマホ で利用するために、 スマホ 上で動く前処理のコードを実装しているのですが、Kotlinを開発しているJetBrains社が開発しているKotlin DataFrameはデータサイエンスに必要な基本的な処理をサポートしているので非常に有効なライブラリですね。 ただ今年に入るまで、Kotlin DataFrameはおろかKotlinさえも書いたことがなかったので、 Python やPandasとの使用感の違いに戸惑いましたね。そこで、早く慣れるために、UdemyでKotlinの基礎を学びつつ、PandasとKotlin DataFrameの操作の対応表を実装時の チートシート として 公式ドキュメント から作成しました。 日ごろPandasの操作に慣れていると、一覧になっているPandasの処理からKotlin DataFrameの処理を引けると便利ですね。毎回DataFrameやDataColumn、それらが提供するメソッドの仕様を調べるより、実装速度が格段に上がりました。 Pandas ⇔ Kotlin DataFrame対応表 今回の実装で自分が使用した処理から(Pandas的に)基本的なものを抜粋しています。無理やり表にしているので改行が変ですがご容赦ください。 処理 pandas Kotlin DataFrame ドキュメント データフレーム生成 df = pd.DataFrame({ "A": [2, 4, 6, 8, 10], "B": [3, 6, 9, 12, 15], "C": [5, 10, 15, 20, 25], "G": ["a", "b", "c", "a", "b"] }) var df: DataFrame = dataFrameOf("A", "B", "C", "G")( 2, 3, 5, "a", 4, 6, 10, "b", 6, 9, 15, "c", 8, 12, 20, "a", 10, 15, 25,"b") https://kotlin.github.io/dataframe/createdataframe.html データフレーム読み込み df = pd.read_ csv ('hogehoge. csv ') var df:DataFrame = DataFrame.read("hogehoge. csv ") https://kotlin.github.io/dataframe/read.html 冒頭表示 df.head() df.head()   データフレームを縦に結合 pd.concat([df1, df2], axis=0) listOf(df1, df2).concat()df1.concat(df2) https://kotlin.github.io/dataframe/concat.html 列を取得 df[”A”] df.getColumn {A} https://kotlin.github.io/dataframe/getcolumn.html 行を取得 df.loc[0] df[0]df[0..0](データフレームとして取得) 行をforループで一づつ取得 for index, row in row_df.iterrows(): print(row["A"]) df.forEach { println(it["A"]) } https://kotlin.github.io/dataframe/iterate.html 行数を取得 len(df) df.rowsCount() https://kotlin.github.io/dataframe/rowscount.html 条件にあうデータを抽出 df.loc[df["G"] == "a", :] df.filter{ G == "a"} https://kotlin.github.io/dataframe/filter.html カラムの値を更新 df[”A”] = df[”A”] - 2 df = df.update{ A }.with{it -2} https://kotlin.github.io/dataframe/update.html カラムの値をDataColumnで取得 df[”A”] df.getColumn {A}df[”A”] https://kotlin.github.io/dataframe/getcolumn.html カラム名 のリストに該当するカラムを選択 list_col = ["A", "B"] df[list_col] val list_col = listOf("A", "B") df.select(*list_col.toTypedArray()) https://kotlin.github.io/dataframe/select.html カラム追加 df['D'] = 1df['D'] = df['C'] - df['B'] df.add("D") { 1 }df.add(”D”) { C - B } https://kotlin.github.io/dataframe/add.html カラムを削除 df. drop ("A") df.remove( "A" ) https://kotlin.github.io/dataframe/remove.html カラム名 を変更 df.rename(columns={'A': 'K'}) df.rename { A }.into( K ) https://kotlin.github.io/dataframe/rename.html 列の値を編集 df[”A”] - 5 df.getColumn {A}.map{ it -5} https://kotlin.github.io/dataframe/getcolumn.html#getcolumngroup カラム名 をリストで取得 df.columns df.columnNames() https://kotlin.github.io/dataframe/columnnames.html カラム名 をリストで変更 df.columns = ["a", "b", "c"] val listCol = df.columnNames() for ( (oldCol, newCol) in listCol.zip(listOf("a", "b", "c"))) {df = df.rename { it[oldCol] }.into(newCol)} https://kotlin.github.io/dataframe/rename.html 値を書き換え # 2 - B列の値を計算し列XとしてAに上書き df["A"] = 2 - df["B"]df = df.rename(columns={"A": "X"}) // 2 - B列の値を計算し列XとしてAに上書き df = df.replace("A").with{ 2 - B named "X" } https://kotlin.github.io/dataframe/replace.html 集計 df.groupby('G').agg(["count", "sum", "mean" ]) df.groupBy("G").aggregate {count() into "count"count{ C > 6 } into "over 6"sum() into "sum"mean() into "mean"} https://kotlin.github.io/dataframe/groupby.html#aggregation 移動平均 を計算 window_size = 2df.rolling(window_size).mean().dropna() var rolling_df = dfval windowSize = 2 for (col in df.columnNames()){rolling_df = rolling_df.replace(col).with {it.values.map { (it as Number).toDouble() }.windowed(windowSize){ it.average() }.plus(List(windowSize - 1) { null }).toValueColumn(col)}}rolling_df = rolling_df.dropNulls() https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/windowed.html 1つ思ったのが、Pandas → Kotlin DataFrameと変換していくとKotlin DataFrame独自の便利な機能が身につきませんね。selectメソッドなんか後述の動画では使いこなせれば便利だと思うんですが、Pandas移植ではあまり使わなかったので残念です(これからめちゃ使う可能性はありますが)。 あと、KotlinはPyhton以上に ラムダ式 が重要だなと感じました。 Python では普段あまり ラムダ式 は使わないので、 ラムダ式 が頻繁に出てくることに苦戦しましたが、使いこなせればより効果的な実装ができるのではないでしょうか? あたりまえですが、Kotlin DataFrameを使いこなすにはKotlin自体の理解が大切だと思いました。 Java は少し書いたことがありますが、基本 Python しか書かない(書けない)人間なのでKotlinがもっと分かれば自由に実装できるのになと思う場面が多かったです。 Kotlin DataFrameによる スマホ 上での前処理の実施 今回は、アプリを使用するユーザーの個人情報をモデルに多く入力するので、モデルによる推論をサーバー上で実行することができず、データの前処理も スマホ 上で処理できるようにしましたが、ONNXとKotlin DataFrameを使えばモバイル端末上でAIモデルによる一連の処理を実行することも可能だと感じました。 もちろん、PCと スマートフォン では処理能力が異なるので、PCでは一瞬で終わる処理も スマホ 上では予想以上に時間がかかります。また、 Python と比較するとモバイル端末上で利用できるKotlinや Android Java のデータサイエンス系のライブラリは充実していないので、 OSS を利用できず自分で実装しないといけなくなる処理が Python よりも増えます。 あと、 コンパイル 言語なのでいちいち コンパイル しないといけないのも開発効率が下がる原因になりますね。逆に、今回 Python のありがたみが身にしみて感じました。 Jupyter Kotlin kernel を使用すれば、Jupytern notebookで インタラクティブ にKotlinのコードをセルごとに実行でき、開発効率がかなり向上しました。PytrhonからKotlinを学ぶ人には必須の環境なんじゃないでしょうか? なんとなくKotlin DataFrameの雰囲気を理解したいなら Kotlin DataFrameのキャッチアップには、こちらの動画がおすすめです。よくデータサイエンス関係の技術の説明で用いられるKaggle チュートリアル のTitanicコンペを題材にKotlin DataFrameでデータ分析をどのように進めるか解説されています。 まずは、PandasとKotlin DataFrameの違いはなにか感覚をつかむことが、重要かなと思います。といいつつ、まだまだ自分もよくわかってないですね。ただ、今回の経験でKotlinや Android アプリ開発 に興味を持ちましたし(もともとモバイルアプリの開発はやりたかった)、AIモデルの応用先として、どこにも持ち運びができて、バッテリーで長時間稼働でき、カメラやマイクと言った入力データを収集するセンサーを持つ スマートフォン は非常に重要なAIモデルの実行環境だと感じました。 最近の スマートフォン の処理能力は以前と比較して格段に向上しましたし、エッジデ バイス として スマートフォン を利用するのではなく、AIサービスをモバイルデ バイス 上で完結して実行するのも今回の件でありだなと感じました。 そのためには、Kotlin DataFrameのようなデータサイエンスに不可欠な処理をサポートするライブラリはより重要性を増すのではないでしょうか? 最後に、私が所属しているAIトランスフォーメーションセンターでは、一緒に働いてくださる方を募集しております。こちらのページに採用に関する内容がまとめられております。また、カジュアル面談の募集もこちらのページからできますので、是非ご覧ください。 https://aitc.dentsusoken.com/recruit/recruit.html それでは。 執筆: @tokuhara.hikaru 、レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
こんにちは。CIT事業部の山田です。 本記事は、下記の方々を対象にした内容となります。 BtoB マーケティング を実施中で、「Marketing Automationツール(以後MAツールを記載)の効果を改善したい or もっと活用していきたい!」方 Salesforce 製品を導入済みで「MAツールの導入 or 製品移行を検討中」の方 また技術的な対応方法詳細に焦点を当てるのではなく、 「MAツール導入効果が見えにくい」「どう改善すべきか」 という課題に対して、BtoB マーケティング で必要なアクション/Account Engagement(旧Pardot)機能との マッピング を中心としています。 1.BtoBマーケティングとMAツール導入 2.BtoBマーケティングで必要なアクション↔︎Account Engagement機能とのマッピング 2-1.リード収集/ セグメント化 2-2.ジャーニーの作成 2-3.リードの引き渡し/選定 2-4.アポ/商談/クロージング/受注 2-5.顧客維持・育成 3.ジャーニーの作成・改善 AE導入によるデータ収集・分析 4.終わりに 参考・補足情報 採用関連リンク 1.BtoB マーケティング とMAツール導入 BtoB マーケティング を実施するために「MAツールを導入してみた」ものの、効果を感じられないという企業様が多く存在します。 参考:Sansan 2023年5月実施「BtoB マーケティング に関する実態調査」 https://jp.corp-sansan.com/news/2023/0601.html MAツールの導入目的は「効率的な受注獲得」にも関わらず、「リード(=見込み)を増やす」ことが目的となってしまっているケースも少なくないのではないでしょうか? ここで1つ質問をさせてください。皆様にとっての「質の良いリード(=見込み客)」とは具体的にどんな”ヒト”でしょうか? 上記を知り得るために、後述の図1:業務プロセス を繰り返し実施し、「効率的な受注獲得」に繋げていくのがBtoB マーケティング の基本だと思います。ただ、これら全てを”人手”で管理するのは作業負荷が高いため、「社内で一元化&効率的に管理したい!」と導入されるのがMAツールです。 言い換えれば、 上記機能を満たしていない(満たせない)MAツールを導入しても意味がない! とはいえMAツールを導入しただけではNG。ジャーニーはじめ、データ分析した結果をもとに改善&質の良いリードを見極めて効率的にアプローチ… etc. と常に改善・チューニングが必要! となります ▼図1:業務プロセス( マーケティング 部門とセールス部門での一般的な対応イメージ) 2.BtoB マーケティング で必要なアクション↔︎Account Engagement機能との マッピング ここからは、 Salesforce 社のMAツール製品の1つ Account Engagement(旧Pardot) (以後「AE」と記載)に絞り、先述した「業務プロセス」ベースでBtoB マーケティング で必要なアクション↔︎AE機能との マッピング を深掘りします。 2-1.リード収集/ セグメント化 ジャーニーを作成・改善する際に、切っても切れないのが「セグメント化」です。リードの属性ごとにジャーニーを分けて管理することが重要です。 AEでは「リスト」や「タグ」でセグメントを管理できます。(詳細は Salesforce社のヘルプ をご参照) セグメント化の一例) リスト 指定した条件や行動ログ、(後述の)Engagementプログラム上での条件分岐結果、外部データのインポート単位、画面上での個別指定 …etc. 多岐にわたる方法でリストを作成できます このリストにて、メルマガ配信先・除外先/Engagementプログラム上の分岐条件 等に流用可能です タグ ヒトやコンテンツ単位で、タグを複数付与することが可能で、このタグを用いてデータを絞り込むことが可能です ▼図2:リストの作成方法(一例) ダイナミックリスト機能 リードに関する情報をもとに、UI上で指定した項目・条件に合致するリードを自動抽出可能 2-2.ジャーニーの作成 ジャーニーを作成する上では、「売り手」(=読者の皆様)と「買い手」のアクションいずれも、条件分岐に設定することが重要となります。 AEの「Engagementプログラム」機能では多岐に渡る項目を、分岐条件として画面上で指定&実現可能です。(詳細は Salesforce社のヘルプ をご参照) Engagement プログラム AE側で管理しているコンテンツ・データ項目だけでなく、 Salesforce 側へのデータ連携なども、ジャーニーとして指定することも可能です。 さらにインテグレーションを行えば、3rd.Party連携側でのアクション結果(Engagementプログラム上では「外部アクティビティ」と表現)を分岐条件に設定することも可能です! 3rd.Party連携の利用例) ビデオ動画の視聴、Webinar受講、アンケート回答結果、SMS連携 …etc. から連携された値をもとに、条件分岐を設定可能 ▼図3:Engagement プログラム 2-3.リードの引き渡し/選定 マーケからセールスへリードを引き渡す際、リードの「関心度」「マッチ度」のスコアリングが重要です。営業側でリード選定できる情報が SFA ・ CRM 側に連携されていないと、無駄なアポ・商談が発生します。 AEでは Salesforce とコネクタ同期しており、AEで取得したデータを Salesforce 側( SFA ・ CRM )ににシームレスに連携&管理できることはもちろん、 Salesforce 側機能を活用して アサイ ンすることなども可能です リードの引き渡し/選定 の一例) AE側で取得・スコアリングしたデータを、 Salesforce 側に同期&参照可能 AE→SFにレコードを新規作成・同期する際に、営業担当者/キュー を自動 アサイ ン (AEの複数機能で本業務に関する設定が可能です。本記事ではオートメーションルール機能に絞っております) ▼図4:AE側で取得・スコアリングされたレコード ▼図5: Salesforce 側でのレコード表示 ▼図6:AE側で指定した条件に応じて、営業担当者/キュー を自動 アサイ ン 一例)「オートメーションルール」機能を用いて、自動 アサイ ンするようAE上で設定 2-4.アポ/商談/クロージング/受注 B2B マーケティング の場合、「リード」の獲得・育成においてDMU(Decision Making Unit)のどのタイプが「リード」になるかは、売り手側ではコン トロール できません。そのため受注までこぎつけるためには、DMUの把握も必要です。 しかし、DMUを把握し決裁者を特定したとしても、決裁者にのみ刺さるコンテンツ作成だけでは不十分です。 DMUのタイプに応じて興味・関心が異なるため、それぞれに適したコンテンツを作成するのが重要となります。 AEでは、容易に様々なコンテンツを作成& Salesforce と横断したコンテンツ流用も可能です。 標準機能 Lightningビルダーを用いて、 ドラッグ&ドロップ での直感的なコンテンツ作成が可能です もちろん、HTMLや CSS を用いた、細かいデザイン・レイアウトを実現可能なコンテンツ作成も可能です カスタマイズ 加えて、 Lightning Web Components ( ※2)を活用すれば、SalesfoorceとAEでの横断コンテンツとして多岐に渡り流用することも可能です ▼図7:Lihghtning ビルダーを用いたコンテンツ作成 2-5.顧客維持・育成 この「顧客維持・育成」プロセスについては「データ収集・分析/ 改善」と大きく関係しており、AEで利用する機能説明も重複してしまうため、次の章でまとめて説明します。 3.ジャーニーの作成・改善 AE提供機能を利用すれば、業務プロセスを自動化でき人手による管理削減に繋げられることは、ご理解いただけたかと思います。ただ実際にMAを導入した場合、次に障壁となるのは「データ収集・分析/ 改善」と「ジャーニーの作成・具体的な改善方法」かと思われます。 AEでは「収集・分析」を実施できる機能が購入時に付いてくる(※2)ため、導入後すぐにこのサイクルを実践できます。 ▼図8:一般的に推奨されている、データ収集・分析/ ジャーニー作成・改善方法 AE導入によるデータ収集・分析 AEでは、標準レポートと ダッシュ ボードが提供されるため、以下の視点でデータ収集・分析が可能です。【図9参照】 メールマガジン (一例) 開封 数、クリック数、バウンス率、CTRなど LP(一例) 訪問数、クリック数など さらに、AE購入時に付属してくるライセンスや機能(※2)を活用&カスタマイズすることで、より詳細なレポートと ダッシュ ボードを作成し視覚化できます。 CRM Analytics (旧:Tableau CRM) の レシピ機能 を用いれば、 Salesforce のユーザー情報とAEで取得したアクションを結合したデータを Analytics Studio 上で分析可能です。【図10参照】 もちろん、AE上で作成・管理しているコンテンツ単位での分析だけでなく、 Google Tag Manager や Google Analytics 側で一元管理・分析することも可能です。 ▼図9:AE標準レポート(配信したメール単位での分析) ▼図10:Analytics Studioでの分析 <本記事上の用語補足> ※詳細は Salesforce 担当営業等にご確認ください ・ CRM Analytics(旧:Tableau CRM および Einstein Analytics) = データ分析ツールとしての製品名 ・ B2B Marketing Analytics =  CRM Analytics上のアプリケーション名(≒フォルダー名) ・Analytics Studio =  CRM Analytics上の機能名(≒アプリケーションランチャー名) 4.終わりに 簡易ではありますが、AEで下記が実現可能な理由を"業務プロセスベース"でAE機能と マッピング しつつ説明いたしました。 「BtoB マーケティング で必要なアクションをカバー可能」 「ジャーニーをはじめ、データ分析した結果をもとに改善&質の良いリードを見極めて効率的にアプローチ可能」 ただし要件に応じて、やはりAE標準機能だけではカバーしきれずにカスタマイズが必要な箇所も一部存在します。 ▼図11:アクション&要件と、AE機能(標準/カスタマイズ可否)の マッピング 表(一例) 弊社では、Salesfoce側のカスタマイズを含む、横断的なAccount Engagementの導入・改善サポートも可能です。 「BtoB マーケティング を実施中で、AEの効果を改善したい or もっと活用していきたい方」 「 Salesforce 製品を導入済みで、MAツールの導入or移行を検討中の方」 上記に該当する方は是非、下記リンクよりお気軽にご相談ください! https://www.dentsusoken.com/request/cb2b 参考・補足情報 (※1)… Salesforce から提供されているSketchの プラグイン を使用してデザイン( モックアップ )を制作してみた 記事も公開済 です。ご興味のある方は是非こちらもご参照ください。 (※2)…ご利用製品のエディション・オプション購入可否に応じて、一部機能の利用可否が異なります。既にAE導入済のお客様は、 Salesforce 社の担当営業等にご確認ください。 採用関連リンク 私たちは一緒に働いてくれる仲間を募集しています! コミュニケーションIT 執筆: @yamada.shiori 、レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
グループ経営ソリューション事業部社員の水上です(24年4月から3年目になりました!)。昨年、 日商簿記 1級を受験し合格することができました。この記事では、IT コンサルタント として働く私が簿記1級を受験した経緯や学習方法、資格取得して良かったこと・期待外れだったことをご紹介します。 <受験した経緯> ▶仕事内容のご紹介 まずは私の仕事内容について、簡単にご紹介します。 私が所属するグループ経営 コンサルティング 第1ユニット(GMC1ユニット)は、STRAVISという 連結会計 ソリューションを扱う部署です。私はその中で、STRAVISの コンサルタント として、システムの導入PJやセールスに携わっています。導入PJでは 経理 部のお客様とご相談しながら要件を決定したり、セールスではシステム導入を通じて 経理 部の連結決算業務がどのように効率化されるのかをご説明したりします。そのため、主に 経理 部のお客様と関わることが多い業務です。 詳細はこちらの記事 新卒3年目社員の働き方紹介(GMS事業部) にありますので、よろしければご覧ください! ▶受験を決意したきっかけ 簿記1級を決意したきっかけは、 経理 部のお客様の話す内容を正しく理解できるようになりたいと考えたことです。配属後はじめて アサイ ンされたPJでは、お客様の説明内容(主に 経理 業務について )をなかなか理解できず、議事録作成にかなり時間がかかってしまっていました。 そこで、残業がまだ少ない1-2年目の内に、当時の自分からすればちょっとハイレベルな簿記1級試験にチャレンジしてみよう!と考えました。 < 日商簿記 1級合格への道のり> 簿記1級に挑戦するぞ!と決意したのが2022年12月、試験に合格したのが2023年11月(合格発表は2024年1月)でした。受験期間が約1年間と長期のため、時期により取り組んでいた内容が異なります。 ▶試験結果 2023年6月   164回試験:38点(不合格) 2023年11月 165回試験:88点(合格) ※ボーダー:70点 ▶学習記録 2023年1月〜2023年11月 1月  2級復習 2月-5月  テキスト読込&確認問題挑戦 5月-6月  過去問演習 7月    休憩 会計本を読む等のみ 8月-11月 問題演習※ ※基礎から過去問レベルまで満遍なく、商会:工原を6:4程度でこなす。 ▶学習時に取り組んで良かったこと ①勉強時間の記録・公表 (8月以降) 6月試験の結果を受けて、さすがにまずいと思い勉強時間の記録を始めました。 今まで「勉強は量より質、理解してるかどうかが大切」と思って重要視していなかったのですが、すごく効果的でした。社会人はそもそも勉強時間をなかなか確保できていないことが多いので、まずはそれを可視化するのがオススメです。 私は、記録を始めてから「そもそも勉強時間を確保できていない」ことに気が付いたので、自習室を契約して土日は必ず行くようにしていました。 使用アプリ:YPT 勉強時間を記録して統計を確認できます。任意のグループに所属すると、メンバーの学習状況も分かります。「この人めっちゃ勉強してる、私も頑張らなきゃ!」と勝手にライバル認定して勉強していました。 ②読書 勉強する気持ちが起きなかったときは、会計本を読む等をして勉強から逃げていました。現実逃避のつもりでしたが、会計の基礎の部分への理解度がぐっと高まったのでとても良かったです。資格勉強では「どう点を取るか」についつい意識がいきがちですが、時間がある時にはそもそもなぜそのような決まりになっているのか?を本でじっくり勉強するのも、土台作りには有益だと思います! ↑おすすめの本です! <業務への影響> 簿記1級の勉強を通じて、「システムで何を実現するのか?」のゴール部分をよく理解できたことで、システムの機能への理解が深まりました。それによって、システムのデモンストレーションを実施する際にも、データを取捨選択したり自分で考えて入れたりすることもできるようになりました。また、当初の目的であった「お客様の話をちゃんと理解できるようになりたい」についても、期待する成果が得られたと感じています。例えばセールスでも、「お客様からの質問を聞き、求める情報を理解して説明する」という一連の流れをスムーズに行えるようになってきました。 簿記1級を勉強しても期待外れだったこともありました。簿記1級取得によりシステムのほとんどを理解できるのではないかと期待していましたが、そんなことはなかったです。例えば「このデータは小数点以下〇桁で持っている」「処理Aと処理Bは必ずA→Bの順に処理が走る」など、保持するデータ種類や処理ロジックなどのシステム理解はまだまだ不足していると感じています。またお客様の話を理解することにおいても、学習簿記と実務とでは異なる点も多いためまだまだ勉強していくべきことが多いです。 ただ、それらは学習簿記で学んだ土台があれば隙間を埋めていくようにインプットしていけば良い事項であり、また土台があることでインプット効率も上がるのではないかとも考えています。総じて、簿記1級取得は今のところ業務に大変役立っています! <最後に> この記事では、IT コンサルタント として働く私の簿記1級受験体験談をご紹介しました。ひとくちに IT技術 者といっても、情報系に特化した人はもちろん、会計系に特化した私みたいな人もいるんだ、ということが伝わっていれば幸いです。 就活生の皆さんの中には、情報系専攻ではないけれど 電通 総研に興味を持ってくださっている方もいらっしゃると思います。入社してから情報系の道を極めるもよし、会計系など独自路線を極めるもよし!いろんな人が楽しく働いている会社です。興味を持ってくださった方は、以下のページをぜひご覧ください。 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 連結会計システム導入プロジェクトマネージャー <連結会計業務領域>業務スペシャリスト/BPOリーダー ◎IT業界未経験者歓迎 (大阪)会計システム導入PM/メンバー(財務会計/経営管理/連結会計)    株式会社電通総研 新卒採用 執筆: @mizukami.koharu 、レビュー: @akutsu.masahiro ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 Amazon Translate による GuardDuty の説明の 機械翻訳 電通 総研では社内向け クラウド SOC として、案件が管理している AWS アカウントで発生した GuardDuty アラートを集中して監視し、担当者へ通知しています。( 参考 ) GuardDuty 検出結果には説明(Description)フィールドがあり、検出内容の要約が文章で示されています。 (例) "Description": "APIs commonly used in Impact tactics were invoked by user AssumedRole : my-role under unusual circumstances. Such activity is not typically seen from this user.", 英語だと分かりづらいことがあるので、案件担当者に通知する時には Amazon Translate のリアルタイム翻訳を利用して、日本語に 機械翻訳 した説明も合わせてお知らせしています。 import { TranslateClient , TranslateTextCommand , TranslateTextCommandInput } from "@aws-sdk/client-translate" ; const client = new TranslateClient (); const command = new TranslateTextCommand ( { Text : "APIs commonly used in Impact tactics were invoked by user AssumedRole : my-role under unusual circumstances. Such activity is not typically seen from this user." , SourceLanguageCode: "en" , TargetLanguageCode: "ja" , } ); const result = await client.send ( command ); console .log ( result.TranslatedText ); 翻訳結果 インパク ト戦術でよく使われる API は、通常とは異なる状況下で AssumeDrole: my-role というユーザーによって呼び出されました。このようなアクティビティは、通常、このユーザーからは見られません。 概ね自然な日本語に翻訳してくれている印象ですが、一部の用語が(翻訳前からそもそも)分かりにくいことがありました。 例えば「 インパク ト戦術」と言われてもすぐにピンとくる人はあまりいないでしょう。(原文でも Impact tactics で分かりづらい。Impact というのは GuardDuty において脅威の目的によるカテゴリ分けの一種である。) カスタム用語で Amazon Translate の翻訳をカスタマイズ Amazon Translate には カスタム用語 という機能があり、特定の用語の翻訳を事前に登録しておくことができます。これを利用すると翻訳文をより分かりやすくすることができると思いました。 その際、既存の継続的デリバリーの仕組みを利用するために、カスタム用語の登録は CDK で行いたいと思いました。 まずカスタム用語の翻訳の マッピング を CSV ファイルで用意します。 今回は「Impact tactics 」をより説明的に「データやプロセスの改ざんが目的」という翻訳に割り当てます。 terminology. csv en,ja Impact tactics,データやプロセスの改ざんが目的 Persistence tactics,AWS環境への不正アクセスを維持することが目的 あとはこれを Translate に登録するわけですが、残念ながら Translate サービスのリソースは CloudFormation による作成がサポートされていません。(2024/3時点) こんな時はカスタムリソースを利用します。シンプルな AWS API の呼び出しを行うカスタムリソースなら、CDK の AwsCustomResource コンスト ラク トで簡単に構築することができます。 // カスタムリソースが使用するポリシー const policyStatement = new iam.PolicyStatement ( { resources: [ "*" ] , actions: [ "translate:ImportTerminology" , "translate:DeleteTerminology" ] , effect: iam.Effect.ALLOW , } ); // 用語ファイルを読み込み const terminology = fs.readFileSync ( "terminology.csv" , "utf-8" ); const terminologyName = "my-terminology" ; // カスタムリソース作成 new AwsCustomResource ( this , "Terminology" , { // リソース作成時:importTerminology APIでカスタム用語を登録 onCreate: { service: "Translate" , action: "importTerminology" , physicalResourceId: PhysicalResourceId. of( "translate-terminology" ), parameters: { Name: terminologyName , MergeStrategy: "OVERWRITE" , TerminologyData: { File: terminology , Format: "CSV" } , } , } , // リソース更新時:同じくimportTerminology APIでカスタム用語を上書き onUpdate: { service: "Translate" , action: "importTerminology" , parameters: { Name: terminologyName , MergeStrategy: "OVERWRITE" , TerminologyData: { File: terminology , Format: "CSV" } , } , } , // リソース削除時:deleteTerminology APIでカスタム用語を削除 onDelete: { service: "Translate" , action: "deleteTerminology" , parameters: { Name: terminologyName , } , } , policy: AwsCustomResourcePolicy.fromStatements ( [ policyStatement ] ), } ); デプロイ後、 AWS マネジメントコンソールからもカスタム用語が登録されていることが確認できます。 カスタム用語を利用して翻訳を実行させると、より分かりやすい説明文になりました。 const command = new TranslateTextCommand ( { Text : "APIs commonly used in Impact tactics were invoked by user AssumedRole : my-role under unusual circumstances. Such activity is not typically seen from this user." , SourceLanguageCode: "en" , TargetLanguageCode: "ja" , TerminologyNames: [ "my-terminology" ] , // 登録した用語を使用させる } ); const result = await client.send ( command ); console .log ( result.TranslatedText ); 翻訳結果 データやプロセスの改ざんが目的でよく使われる API は、通常とは異なる状況下で AssumeDrole: my-role というユーザーによって呼び出されました。このようなアクティビティは、通常、このユーザーからは見られません。 私たちは一緒に働いてくれる仲間を募集しています! キャリア採用 クラウドアーキテクト 執筆: @kou.kinyo 、レビュー: @yamada.y ( Shodo で執筆されました )
アバター
はいどーもー! コミュニケーションIT事業部の宮澤響です! 本日は4月1日! ということで、(4月入社前提の新卒一括採用形式を導入している企業の)新入社員のみなさま、入社おめでとうございます! 慣れない環境で戸惑うことや大変なことも多いかもしれませんが、私としては 心身ともに健康でいてくれればそれだけで100点満点 かなと思っていますので、無理せずほどほどに頑張っていただければと思います…! さて、本記事では、Teamsカレンダーで誤って オンライン会議 オンで登録してしまった予定をあとから オンライン会議 オフに変更する小技をご紹介します! エイプリルフールではありますが、嘘は記載していませんのでご安心ください…!笑 前置き①:こんな経験ありませんか 前置き②:こうならないために 本編 結論 余談 おわりに 前置き①:こんな経験ありませんか あなたのTeamsカレンダーのある時間帯に、Zoomで開催される打ち合わせの予定が登録されています。 打ち合わせの開催時刻になったので、あなたは 参加 ボタンから、打ち合わせに参加しました。 しかし、開始時刻を過ぎても、一向に他の参加者が入室してきません。 不審に思ったあなたは、自分が開催時刻を勘違いしていないか、予定の詳細画面を今一度確認します。 そこであなたはこの打ち合わせがZoom開催であることに気づき、詳細画面に記載されていたリンクから慌ててZoomでの打ち合わせに参加し直します。 「遅くなってしまい申し訳ございません…!」 前置き②:こうならないために 上述の予定は、デフォルトのまま オンライン会議 オンで登録されていたため、 参加 ボタンが表示されていました。 そのため、 参加 ボタンが表示されないように、 オンライン会議 をオフに設定しておけば、上述の事態は防げたことになります。 しかし、予定の詳細画面を確認すると、 オンライン会議 のトグルボタンがオンのままグレーアウトしています。 そのため、トグルボタンを操作して直接 オンライン会議 をオフに変更することはできないようです。 (※複数環境で確認しましたが、バージョンやテナントの権限設定などに依存している可能性はあります。) もちろん、この予定自体を丸ごと削除して、最初から オンライン会議 をオフに設定した予定を登録し直すことは可能です。 しかし、多数の参加者が含まれる予定の場合には、登録する側も参加者全員を入力し直す必要があるため面倒ですし、登録される側も必要に応じて 承諾 をし直す必要があるため面倒です。 また、通知メールも再送信されることになります。 そのため、可能であれば、既に登録されている予定を オンライン会議 オフに設定変更したいところです。 ということで、そのような方法がないか、次のセクションで試行錯誤してみます。 本編 まずはTeamsカレンダーに 宮澤テスト という予定を登録してみます。 登録された予定を確認すると、 場所 が Microsoft Teams Meeting となっていて、 参加 ボタンが表示されています。 これは、上述のとおり、デフォルトでは オンライン会議 がオンになっているためです。 そこで、試しに 場所 の欄の Microsoft Teams Meeting を削除して保存してみます。 更新した予定を確認すると、 場所 の欄こそ削除されているものの、 参加 ボタンは表示されたままです。 また、詳細画面でも、 オンライン会議 がオンのままです。 そこで、次は 会議の詳細 の欄の記述を削除して保存してみます。 更新した予定を確認すると、 参加 ボタンが表示されておらず、 オンライン会議 がオフになっています。 どうやら、 会議の詳細 の欄の記述が、 オンライン会議 のオンオフと関係しているようです。 今度は、 場所 の欄の Microsoft Teams Meeting を残した状態で、 会議の詳細 の欄の記述を削除してみます。 更新した予定を確認すると、 参加 ボタンが表示されておらず、 オンライン会議 がオフになっています。 また、 場所 の欄の Microsoft Teams Meeting が自動で削除されています。 以上の結果から、 オンライン会議 をオフにするためには、 場所 の欄の Microsoft Teams Meeting は手動で削除する必要がなく、 会議の詳細 の欄の記述のみを削除すればよいことを確認できました。 結論 Teamsカレンダーで誤って オンライン会議 オンで登録してしまった予定をあとから オンライン会議 オフに変更するためには、 会議の詳細 の欄の記述を削除 すればOKです! 余談 厳密には、 会議の詳細 の欄の記述に含まれる 今すぐ会議に参加する の部分のみを削除(またはリンク先を別のURLに変更)するだけで、 オンライン会議 をオフにできます。 記述を残しておく必要性はおそらく皆無(むしろ削除した方が綺麗)ですが…。 おわりに 本記事では、Teamsカレンダーで誤って オンライン会議 オンで登録してしまった予定をあとから オンライン会議 オフに変更する小技をご紹介しました! 最初から オンライン会議 オフで登録しておけば実施する必要のない操作ではありますが、いざというときのために頭の片隅にでも入れておいていただけると幸いです。 最後までお読みいただき、本当にありがとうございました! 私たちは同じ事業部で共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! <電通×IT>電通グループ基幹システムプロジェクトマネージャー エンタープライズ向けDX推進リーダー/エンジニア <電通×IT>クラウドアーキテクト <電通×IT>アプリケーションアーキテクト 製品・プラットフォーム開発エンジニア 執筆: @miyazawa.hibiki 、レビュー: @ozaki.hisanori ( Shodo で執筆されました )
アバター
こんにちは。XI 本部 AIトランスフォーメーションセンター 所属の山田です。 今回は比較的にライトな内容で、 GitHub Actionsのワークフロー設計のTipsを紹介したいと思います。 紹介するワークフロー ここで紹介するワークフローは、Node.js系アプリケーションで静的解析、テスト、ビルド実行を想定したものになります。 依存関係インストール用のジョブ このワークフローの特徴は、依存関係の解決に重点を置き、取得した依存関係をキャッシュエントリに追加することを目的とするsetupジョブを配置している点です。 setupジョブの定義部分を抜粋すると以下になります。 jobs: setup: runs-on: ubuntu-latest defaults: run: shell: bash steps: - name: Check out repository uses: actions/checkout@v3 - name: Set up Node.js uses: actions/setup-node@v2 with: node-version: ${{ env.NODE_VERSION }} cache: 'yarn' cache-dependency-path: '**/yarn.lock' - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - uses: actions/cache@v3 id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) with: path: | **/node_modules ${{ steps.yarn-cache-dir-path.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - name: Install dependencies if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn setupジョブでの依存関係の解決部分でもキャッシュがヒットする場合は、キャッシュを利用するようにしています。 静的解析、テスト、ビルドのジョブの並列実行 静的解析、テスト、ビルドの3つのジョブはそれぞれ独立して実行可能なため並列に実行します。 ※ GitHub Actionsでは依存関係を指定しない限りジョブは並列に実行されます。 それぞれのジョブ定義で needs: setup と記述します。 lint: needs: setup # 省略 test: needs: setup # 省略 build: needs: setup # 省略 ジョブの依存関係にsetupジョブに指定しているため、各ジョブの依存関係の解決時には常にキャッシュが利用され、処理がスキップされるため静的解析、テスト、ビルドが速やかに開始されます。 ワークフローの実行時間 今回の例のワークフローの実行時間を確認すると、setupジョブの2分36秒、並行実行されるジョブの中で最も時間のかかったbuildジョブの実行時間1分30秒を加え、4分6秒ほどで開発者は静的解析、テスト、ビルドの結果を確認できます。 なお上記はsetupジョブでキャッシュがヒットしなかった場合になります。 setupジョブでキャッシュがヒットする場合も確認してみると、setupジョブで47秒、並行して実行されるジョブの中で最も長かったbuildジョブの1分12秒を足して1分59秒ほどで開発者は静的解析、テスト、ビルドの結果を見ることができます。 ただしジョブの実行時間の請求は秒数が切り上げで計算される点には注意が必要です(画像中のBillable time部分をご参照ください)。 まとめ 本記事では GitHub Actionsのワークフローを設計する際に依存関係の解決だけを行うsetupジョブを用意することで後続のジョブを並行して速やかに実行するというTipsを紹介しました。 記事ではNode.jsを例に説明しましたが、 Java やGo、 Python などの多くの プログラミング言語 で活用できる方法だと思います。ぜひ試してみてください。 参考 依存関係をキャッシュしてワークフローのスピードを上げる - GitHub Docs 執筆: @yamada.y 、レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
こんにちは。 電通 総研コーポレート本部システム推進部の山下です。 AWS でサービスを構築していると、固定IPでサービスを公開したい場合があります。 DNS などのIPで通信先を指定するようなサービスでは、 AWS ではNetwork LoadBalancer(NLB)を利用するのが一般的です。 本記事では、 AWS Cloud Development Kit (CDK) を使ってElastic Container Service (ECS) とNLBを組み合わせてサービスを公開する方法について紹介します。 CDKのNetworkLoadBalancedFargateServiceを使ってサービスを公開する CDKの NetworkLoadBalancedFargateService を利用すると、簡単にNLBとFargateを組み合わせたサービスの構築が出来ます。 しかし、以下のような形でこの関数だけを呼び出して cdk deploy を実施すると失敗します。 const loadBalancedFargateService = new NetworkLoadBalancedFargateService ( this , "Service" , { memoryLimitMiB: 1024 , cpu: 512 , taskImageOptions: { image: ecs.ContainerImage.fromRegistry ( "nginx" ) } , } ); これは、 NetworkLoadBalancedFargateService で構築されたFargateにはSecurityGroupが適切に設定されておらず NLBからのヘルスチェックに失敗してしまうため cdk deploy にも失敗してしまうためです。 例えば、以下のような記述を追加して特定のポートに対するアクセスを許可する必要があります。 const myService = loadBalancedFargateService.service ; const mySg = myService.connections.securityGroups [ 0 ] ; mySg.addIngressRule ( ec2.Peer.ipv4 ( '0.0.0.0/0' ), ec2.Port.tcp ( 80 ), 'SSH frm anywhere' ); これで、NLBからのヘルスチェックが動くようになり cdk deploy が成功します。 UDP のサービスをNLBで公開する NLBで UDP のサービスを公開するには、NLBのヘルスチェックは TCP を使った通信で実施されるということに注意する必要があります。 つまり、 UDP のサービス単体ではNLBのヘルスチェックに応答することが出来ず公開できないということになります。 ECSからサービスを公開する場合にもこれは同様です。 ECSのServiceに2つのタスクを起動し片方をヘルスチェック用として公開するなどの工夫を行う必要があります。 例えば以下のように、 UDP でサービスを公開するタスクのタスク定義を作ります。 // ECS Clusterの作成 const cluster = new ecs.Cluster ( this , 'udp-service-cluster' , { vpc: vpc // 事前に作成してあるVPC } ); const taskDef = new ecs.FargateTaskDefinition ( this , "udp-task" , { family: 'udp-task' , memoryLimitMiB: 512 , cpu: 256 , executionRole: execRole , // 事前に定義してあるRole taskRole: taskRole // 事前に定義してあるRole } ); const repository = Repository.fromRepositoryName ( this , "udp-service-registry" , 'udp-service' ); taskDef.addContainer ( "udp-service-container" , { containerName: "udp-service" , image: ecs.ContainerImage.fromEcrRepository ( repository , 'latest' ), portMappings: [{ containerPort: 1234 , protocol: ecs.Protocol.TCP }] } ) // CDK上でUDPしか公開していないタスク定義はエラーになるので以下のように addPropertyOverrideで対応する const td = taskDef.node.defaultChild as ecs.CfnTaskDefinition ; td.addPropertyOverride ( 'ContainerDefinitions.0.PortMappings.0.Protocol' , 'udp' ); これで、 UDP だけを公開するタスク定義は作成できます。 これに加えて、ヘルスチェック用に追加でコンテナを稼働させるように変更します。 ここでは、以下のようなシェルの ワンライナー で動作するような簡易サーバを動作させてヘルスチェックに応答させることにします。 8080番ポートで動作し、'ok' と返すだけのサーバ(もどき)です。 これでは実際のサービスのヘルスチェックにはなりません。あくまでNLBからのヘルスチェックに応答させるためのものです。 死活監視は別の方法で担保する必要があることに注意してください。 const healthCheckcontainer = taskDef.addContainer ( "healthcheck-container" , { image: ecs.ContainerImage.fromRegistry ( "busybox:latest" ), entryPoint: [ "sh" , "-c" ] , command: [ "while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo 'ok'; } | nc -l -p 8080; done" ] } ) healthCheckcontainer.addPortMappings ( { containerPort: 8080 , } ); そして、このタスク定義を使ったFargateServiceを作ります。 アクセスを許可するためのセキュリティグループも合わせて作ります。 const secGroup = new ec2.SecurityGroup ( this , 'ntp-sg' , { vpc: vpc , allowAllOutbound: true } ); secGroup.addIngressRule ( ec2.Peer.ipv4 ( '0.0.0.0/0' ), ec2.Port.udp ( 1234 ), 'for UDP service' ); secGroup.addIngressRule ( ec2.Peer.ipv4 ( '0.0.0.0/0' ), ec2.Port.tcp ( 8080 ), 'for healthCheck' ); const fargateService = new cdk.aws_ecs.FargateService ( this , 'udp-service' , { cluster , taskDefinition: taskDef , assignPublicIp: false , securityGroups: [ secGroup ] , enableExecuteCommand: true , } ); ここまでで、 UDP サービスを公開するFargateは完成しました。 あとは、公開に利用するNLBの構築、ターゲットグループなどの設定を行う必要があります。 // NLB を作成する const nlb = new NetworkLoadBalancer ( this , 'udp-service-nlb' , { internetFacing: false , // 今回は内部向けサービスを想定 vpc: vpc , crossZoneEnabled: false , ipAddressType: IpAddressType.IPV4 , } ); const listener = nlb.addListener ( 'udp-service-listener' , { port: 1234 , protocol: lb.Protocol.UDP } ); listener.addTargets ( 'udp-service-tg' , { port: 1234 , protocol: lb.Protocol.UDP , targets: [ fargateService.loadBalancerTarget ( { containerName: "udp-service" , containerPort: 1234 } ) ] , deregistrationDelay: Duration.seconds ( 300 ), // 作成したヘルスチェック用のコンテナに通信するよう設定 healthCheck: { port: "8080" , protocol: lb.Protocol.TCP } } ); これで、NLBと作成したFargateの連携ができるようになりました。 ヘルスチェック向けのコンテナ追加などの手間は増えてしまいますが、 NLBと連携させることで可用性や冗長性の確保、IPの固定などが可能になりました。 まとめ 今回はNLBとECSの組み合わせでサービスを公開する際のハマりポイントを紹介してみました。 注意点をまとめますと セキュリティグループの設定を実施しないとヘルスチェックに失敗する UDP のサービスを公開する場合でも TCP でヘルスチェックは実施される この2点となります。 もし、NLBを使ってサービス公開する場面があったら本記事が参考になれば幸いです。 執筆: @yamashita.tsuyoshi 、レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
こんにちは、コーポレート本部 サイバーセキュリティ推進部 セキュアシステムデザイングループの福山です。 今回は、 AWS の脅威検知サービス Amazon GuardDutyで利用できる マルウェア 検出機能「 Malware Protection」について整理してみました。 サーバーにおけるマルウェア対策について Malware Protectionとは Malware Protectionのスキャン手法 従来のアンチウイルスソフトとの比較 有効化・実行する方法 実行型マルウェアスキャンの場合 オンデマンドスキャンの場合 スキャン結果の確認方法 費用 Malware Protectionのユースケース別の活用例 最後に サーバーにおける マルウェア 対策について マルウェア とは、ウイルス、 トロイの木馬 、ワーム、 スパイウェア 、そして近年被害が話題になっている ランサムウェア などの総称です。 マルウェア の脅威は Windows だけに及ばず、近年では Linux のサンプル数も急速に増加傾向にあります。 参考: https://atlasvpn.com/blog/linux-malware-on-a-rise-reaching-all-time-high-in-h1-2022 また、EC2 インスタンス への 不正アクセス などによって マルウェア が仕込まれるケースや、さらにその インスタンス が感染源となり、他のクライアント端末にも マルウェア が広がるケースもあります。 このように、クライアント端末だけでなくサーバー側でも マルウェア 対策が必要になってきています。 そこで AWS のソリューションの一つとして、 Amazon GuardDutyの Malware Protectionをご紹介します。 Malware Protectionとは 以下、 Malware Protectionの概要について要点をまとめてみました。 エージェントレスで動く、フルマネージドな マルウェア 検出オプション EC2とEC2上で動作するコンテナ(ECS、EKS)をスキャン対象とする [ NEW ] 2024年2月より、 AWS 管理のキー(KMS)で暗号化されたEBSもスキャン対象に! 駆除/削除/隔離は実施しない( マルウェア の検出のみ) 一時的にEBSのスナップショットを作成し、それに対してスキャンをかけるためパフォーマンスへの影響なし 実行した分だけ費用が発生する従量課金制となっており、東京リージョンで0.05USD/GB 動的に増減するコンテナワークロードや、通信が制限されたプライベートサブネット上の インスタンス 、停止中の インスタンス に対してもスキャン可能 Security Hub やGuardDutyマルチアカウント構成による集約が可能 Malware Protectionのスキャン手法 スキャン手法は2種類あります。 実行型 マルウェア スキャン オンデマンドスキャン 概要 マルウェア の挙動に関連性のある 特定のGuardDutyアラート をトリガーに実行される ユーザーの任意のタイミングで実行できる スキャンの待ち時間 前のスキャンから24時間空ける必要あり 前のスキャンから1時間空ける必要あり 30日の無料期間 あり なし 従来の アンチウイルスソフト との比較 一般的なエージェント型で有償の アンチウイルスソフト や無償ツールと比較してみました。 Malware Protection 有償の アンチウイルスソフト Microsoft Defender(デフォルト) ClamAV 検出方法 パターンマッチング(スキャンエンジンには、 AWS 独自の検出エンジンと Bitdefender が取り入れられている) パターンマッチング以外に振る舞い検知といった高度な検出手法が可能な製品もある パターンマッチング パターンマッチング マルウェア の駆除/削除/隔離 不可 可 可 可 リアルタイムスキャン できない ※GuardDutyアラートをトリガーにしたスキャンは可 可 可 可 サーバへの負荷 なし あり あり あり ユーザーによる初期設定 AWS アカウント単位で、数クリックで完了 サーバー単位でエージェントのインストールが必要 不要 サーバー単位でエージェントのインストールが必要 ユーザーによる定義ファイルの更新 不要 必要 必要 必要 集中監視の仕組み AWS アカウント単位でリソース設定が必要(マルチアカウント構成やSecurity Hubでのアカウント集約も可能) 集中管理サーバーなどを用いれば標準機能として利用できる製品が多い サーバー単位でのカスタマイズが必要 サーバー単位でのカスタマイズが必要 サポートによる検体の解析 不可 可 可 不可 課金体系 従量課金 定額課金 - - 有効化・実行する方法 実際に Malware Protectionを利用する方法について紹介します。 まずは前提としてGuardDutyを有効化する必要があります。また、通知する仕組みも作っておくことをおすすめします。 参考: https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/guardduty_findings_cloudwatch.html#guardduty_cloudwatch_severity_notification 実行型 マルウェア スキャンの場合 GuardDutyコンソール -> Malware Protection -> GuardDuty が開始する マルウェア スキャン -> 有効にする タグを使用して、特定の インスタンス だけスキャン対象にするといったことも可能 オンデマンドスキャンの場合 GuardDutyコンソール -> マルウェア スキャン -> 新しいオンデマンドスキャンを開始 EC2 インスタンス ARN にて以下ARNを入力し、確認 をクリック arn: aws :ec2:ap-northeast-1:{account_id}:instance/{instance_id} スキャン結果の確認方法 検出後、スキャン結果を確認する方法について紹介します。 GuardDutyコンソール -> マルウェア スキャン にてスキャンステータスが Completed になった後に結果を確認できる スキャン結果の種類はClean(問題なし)、Infected(感染の疑いあり)の2種類 GuardDutyコンソール -> 検出結果から、ファイル名・ファイルパス・ ハッシュ値 などを確認できる 費用 Malware Protectionはスキャンした時だけ費用が発生する従量課金制です。 EBSデータボリュームのスキャン費用 東京リージョンの場合:0.05USD/GB(実際のスキャンサイズの費用が発生する。ボリュームの最大サイズではない) スキャンのために一時的に取得されるEBSスナップショットの費用 デフォルトでは数分後に自動で削除される。 Malware Protectionの ユースケース 別の活用例 Malware Protectionの ユースケース 別の活用例を考えてみました。 アンチウイルスソフト を導入していないケース 既に アンチウイルスソフト を導入して監視の仕組みができているケース 平時 Malware Protection 実行型 マルウェア スキャンを有効にしておく( Windows インスタンス の場合は Microsoft Defenderも有効にしておくとより安心でしょう) アンチウイルスソフト による定期スキャンおよびリアルタイムスキャン+必要に応じて Malware Protection 実行型 マルウェア スキャンを有効にしておく( インスタンス の増減が頻繁に発生する環境でのエージェントの導入漏れ対策として) 有事( 不正アクセス などが疑われる場合) 調査用途として Malware Protection オンデマンドスキャンを実施する( Windows インスタンス の場合は Microsoft Defenderによるスキャンも実施しておくと良いでしょう) アンチウイルスソフト によるスキャン+必要に応じて Malware Protection オンデマンドスキャンを実施する(異なる検出エンジンを用いて検知漏れを防いだり、自らをエージェントの検知から隠蔽する マルウェア をスキャン) メリット 手軽に マルウェア 対策を開始でき、必要最低限のコストに抑えられる アンチウイルスソフト を補完するかたちとして マルウェア 対策を強化できる 注意点 ユーザー側で検知後のアクション(削除/隔離など)が必要。また、 APIで定期的にオンデマンドスキャンを実行する方法 もあるが、費用に注意 アンチウイルスソフト の運用(定義ファイルの更新など)は従来通り対応が必要 最後に GuardDuty Malware Protection自体は2022年7月にリリースされましたが、2024年2月より、 AWS 管理のキー(KMS)で暗号化されたEBSもスキャン対象になったことで、本格的に活用できる状態となりました。 ただし、 Malware Protectionが既存の アンチウイルスソフト に完全に取って代わるかというとそうではありません。 一般的な アンチウイルスソフト はディスクI/Oをトリガーに マルウェア の実行を止めるのに対し、 Malware Protectionは悪意のある挙動を確認した後にスキャンを実施することとなるため、実施タイミング(守備範囲)が異なります。 参考: https://www.trendmicro.com/ja_jp/business/campaigns/aws/column/guarddutymalwareprotection.html とは言え、 マルウェア 対策ができていない状況であれば、有力な選択肢となるのは間違いなさそうです。 以上、GuardDuty Malware Protectionについてでした。 執筆: @fukuyama.kenta ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 当社は2024年1月に社名が「 電通国際情報サービス 」(ISID)から「 電通 総研」に変わりました。 当然、各種システムの変更も社名変更に合わせて行われました。 今回は AWS CDK を利用して構築したある社内向け Web アプリの ドメイン を切り替える際に、意外とサクッと終わった話をしたいと思います。 ドメイン の切り替えは緊張感のある作業ですが、結果的に本番環境での実作業時間はわずか30分で終わりました。 アプリのインフラ構成 切り替え手順 CDKによる切り替え作業: 切り替え前 CDKによる切り替え作業: 事前準備 CDKによる切り替え作業: 本番切り替え 所感 アプリのインフラ構成 今回 ドメイン 切り替えを行った社内向け Web アプリの大雑把なインフラ構成は次の図の通りです。関連する コンポーネント のみを抜き出しており、DB や WAF などは省略しています。また実際は ECS アプリケーションのブルーグリーンデプロイを行っているのですが、これも省略しています。 cool-app.isid.example ドメイン を利用し、ALB のターゲットグループとして ECS サービスを登録しています。 このWeb アプリの ドメイン を cool-app.dentsusoken.example に切り替えます。 切り替え手順 事前準備作業と、本番切り替え作業の2段階に分けて ドメイン 切り替えを実施しました。 事前準備作業として以下を行います。 新 ドメイン のホストゾーンの用意 リダイレクト用ALBの新規作成 新 ドメイン の ACM 証明書作成 新 ドメイン はこの段階ではリダイレクト用ALBを指しており、リダイレクト用ALBは旧 ドメイン に302リダイレクトします。 この時点でアプリケーションへの従来のアクセスには何も影響はありません。 続いて本番切り替え作業では、アプリケーション用ALBの ドメイン を新 ドメイン に変更し、旧 ドメイン はリダイレクト用ALBを指すように切り替えます。 リダイレクト用ALBも新 ドメイン に301リダイレクトするように変更します。 これによりブラウザによる旧 ドメイン へのアクセスは全て新 ドメイン にリダイレクトされるようになります。ところで ALBのURLリダイレクト機能 は元のパス、クエリパラメータを保持したままリダイレクトを実現できるので、ほぼユーザーが意識することなく ドメイン 切り替えを実現できるのは嬉しいです。 CDKによる切り替え作業: 切り替え前 ドメイン 切り替え前は、以下のサンプルコードのような構成でCDKを実装していました。 アプリケーション new MyAppStack ( app , "MyAppStack" , { hostedZoneId: "Z1111111OLDHOSTEDZONE" , hostedZoneName: "isid.example" , webAppRecordName: "cool-app" , } ); スタック(一部のプロパティのみ掲載) // アプリケーション用ALB const alb = new elb.ApplicationLoadBalancer ( this , "Alb" , { vpc , vpcSubnets: { subnetGroupName } , securityGroup , internetFacing: true , } ); // ターゲットグループ const targetGroup = new elb.ApplicationTargetGroup ( this , "TargetGroup" , { vpc , port: 3000 , protocol: elb.ApplicationProtocol.HTTP , targetType: elb.TargetType.IP , } ); // ホストゾーン const hostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "HostedZone" , { hostedZoneId: props.hostedZoneId , zoneName: props.hostedZoneName , } ); // アプリケーション用ALBへのAレコード new route53.ARecord ( this , "WebARecord" , { zone: hostedZone , recordName: props.webAppRecordName , target: route53.RecordTarget.fromAlias (new route53Targets.LoadBalancerTarget ( alb )), } ); // ACM証明書 const certificate = new certificatemanager.Certificate ( this , "Certificate" , { domainName: ` ${ props.webAppRecordName } . ${ props.hostedZoneName } ` , validation: certificatemanager.CertificateValidation.fromDns ( hostedZone ), } ) // ALBリスナー alb.addListener ( "ApplicationListener" , { protocol: elb.ApplicationProtocol.HTTPS , port: 443 , certificates: [ certificate ] , sslPolicy: elb.SslPolicy.TLS13_RES , defaultTargetGroups: [ targetGroup ] , open: false , } ); CDKによる切り替え作業: 事前準備 事前準備段階として、リダイレクト用ALB、新 ドメイン のAレコード、 ACM 証明書などを新規に作成します。 CDKのコード上ではプロパティ名を変更し、新旧どちらの ドメイン を使用しているのか明確に分かるようにしました。 アプリケーション new MyAppStack ( app , "MyAppStack" , { // 旧ホストゾーンのプロパティ名を変更 oldHostedZoneId: "Z1111111OLDHOSTEDZONE" , oldHostedZoneName: "isid.example" , // 新ホストゾーンのプロパティを追加 newHostedZoneId: "Z2222222NEWHOSTEDZONE" , newHostedZoneName: "dentsusoken.example" , webAppRecordName: "cool-app" , } ); スタック // 【変更なし】アプリケーション用ALB const alb = new elb.ApplicationLoadBalancer ( this , "Alb" , { vpc , vpcSubnets: { subnetGroupName } , securityGroup , internetFacing: true , } ); // 【追加】リダイレクト用ALB // ドメイン切り替え前は新ドメインから旧ドメインへ、 // ドメイン切り替え後は旧ドメインから新ドメインへリダイレクトする const redirectAlb = new elb.ApplicationLoadBalancer ( this , "RedirectAlb" , { vpc , vpcSubnets: { subnetGroupName } , securityGroup , internetFacing: true , } ); // 【変更なし】ターゲットグループ const targetGroup = new elb.ApplicationTargetGroup ( this , "TargetGroup" , { vpc , port: 3000 , protocol: elb.ApplicationProtocol.HTTP , targetType: elb.TargetType.IP , } ); // 【変数名変更】旧ホストゾーン const oldHostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "HostedZone" , { hostedZoneId: props.oldHostedZoneId , zoneName: props.oldHostedZoneName , } ); // 【追加】新ドメインのホストゾーン const newHostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "NewHostedZone" , { hostedZoneId: props.newHostedZoneId , zoneName: props.newHostedZoneName , } ); // 【変数名変更】旧ドメインのAレコード new route53.ARecord ( this , "WebARecord" , { zone: oldHostedZone , recordName: props.webAppRecordName , target: route53.RecordTarget.fromAlias (new route53Targets.LoadBalancerTarget ( alb )), } ); // 【追加】新ドメインのAレコード new route53.ARecord ( this , "NewWebARecord" , { zone: newHostedZone , recordName: props.webAppRecordName , target: route53.RecordTarget.fromAlias (new route53Targets.LoadBalancerTarget ( redirectAlb )), } ); // 【変数名変更】旧ドメインのACM証明書 const oldCertificate = new certificatemanager.Certificate ( this , "Certificate" , { domainName: ` ${ props.webAppRecordName } . ${ props.oldHostedZoneName } ` , validation: certificatemanager.CertificateValidation.fromDns ( oldHostedZone ), } ) // 【追加】新ドメインのACM証明書 const newCertificate = new certificatemanager.Certificate ( this , "NewCertificate" , { domainName: ` ${ props.webAppRecordName } . ${ props.newHostedZoneName } ` , validation: certificatemanager.CertificateValidation.fromDns ( newHostedZone ), } ) // 【変数名変更】アプリケーション用ALBのリスナー alb.addListener ( "ApplicationListener" , { protocol: elb.ApplicationProtocol.HTTPS , port: 443 , certificates: [ oldCertificate ] , sslPolicy: elb.SslPolicy.TLS13_RES , defaultTargetGroups: [ targetGroup ] , open: false , } ); // 【追加】リダイレクト用ALBのリスナー redirectAlb.addListener ( "RedirectAlbListener" , { protocol: elb.ApplicationProtocol.HTTPS , port: 443 , certificates: [ newCertificate ] , sslPolicy: elb.SslPolicy.TLS13_RES , open: false , // ALB の機能で旧ドメインへ 302 リダイレクト defaultAction: elb.ListenerAction.redirect ( { host: ` ${ props.webAppRecordName } . ${ props.oldHostedZoneName } ` , permanent: false , } ), } ); CDKによる切り替え作業: 本番切り替え ドメイン 切り替え本番では、ホストゾーンのレコードの指す先と証明書がアタッチされるALBを変更し、リダイレクト用ALBのリダイレクト先 ドメイン も変更しました。 スタック // 【変更なし】アプリケーション用ALB const alb = new elb.ApplicationLoadBalancer ( this , "Alb" , { vpc , vpcSubnets: { subnetGroupName } , securityGroup , internetFacing: true , } ); // 【変更なし】リダイレクト用ALB const redirectAlb = new elb.ApplicationLoadBalancer ( this , "RedirectAlb" , { vpc , vpcSubnets: { subnetGroupName } , securityGroup , internetFacing: true , } ); // 【変更なし】ターゲットグループ const targetGroup = new elb.ApplicationTargetGroup ( this , "TargetGroup" , { vpc , port: 3000 , protocol: elb.ApplicationProtocol.HTTP , targetType: elb.TargetType.IP , } ); // 【変更なし】旧ホストゾーン const oldHostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "HostedZone" , { hostedZoneId: props.oldHostedZoneId , zoneName: props.oldHostedZoneName , } ); // 【変更なし】新ドメインのホストゾーン const newHostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "NewHostedZone" , { hostedZoneId: props.newHostedZoneId , zoneName: props.newHostedZoneName , } ); // 【変更】旧ドメインのAレコード new route53.ARecord ( this , "WebARecord" , { zone: oldHostedZone , recordName: props.webAppRecordName , // 旧ドメインはリダイレクト用ALBをターゲットに転送 target: route53.RecordTarget.fromAlias (new route53Targets.LoadBalancerTarget ( redirectAlb )), } ); // 【変更】新ドメインのAレコード new route53.ARecord ( this , "NewWebARecord" , { zone: newHostedZone , recordName: props.webAppRecordName , // 新ドメインはアプリケーション用ALBをターゲットに転送 target: route53.RecordTarget.fromAlias (new route53Targets.LoadBalancerTarget ( alb )), } ); // 【変更なし】旧ドメインのACM証明書 const oldCertificate = new certificatemanager.Certificate ( this , "Certificate" , { domainName: ` ${ props.webAppRecordName } . ${ props.oldHostedZoneName } ` , validation: certificatemanager.CertificateValidation.fromDns ( oldHostedZone ), } ) // 【変更なし】新ドメインのACM証明書 const newCertificate = new certificatemanager.Certificate ( this , "NewCertificate" , { domainName: ` ${ props.webAppRecordName } . ${ props.newHostedZoneName } ` , validation: certificatemanager.CertificateValidation.fromDns ( newHostedZone ), } ) // 【変更】アプリケーション用ALBのリスナー alb.addListener ( "ApplicationListener" , { protocol: elb.ApplicationProtocol.HTTPS , port: 443 , // 新ドメインの証明書に変更 certificates: [ newCertificate ] , sslPolicy: elb.SslPolicy.TLS13_RES , defaultTargetGroups: [ targetGroup ] , open: false , } ); // 【変更】リダイレクト用ALBのリスナー redirectAlb.addListener ( "RedirectAlbListener" , { protocol: elb.ApplicationProtocol.HTTPS , port: 443 , // 旧ドメインの証明書に変更 certificates: [ oldCertificate ] , sslPolicy: elb.SslPolicy.TLS13_RES , open: false , // ALB の機能で新ドメインへ 301 リダイレクト defaultAction: elb.ListenerAction.redirect ( { host: ` ${ props.webAppRecordName } . ${ props.newHostedZoneName } ` , permanent: true , } ), } ); 以上で難なく ドメイン 切り替え作業が完了しました。あとは DNS のキャッシュが切れたら新 ドメイン でアプリにアクセスできるようになります。 本番切り替え作業にかかった時間はCDKのデプロイから切り替え後の接続確認まで含めても30分ほどでした。(実際はもっと色々なイン フラリ ソースに関わる作業があったので、本記事に記載した構成だけならもっと早く終わっていたと思います) 所感 これまでの内容では省いていますが、各作業段階においてもちろんステージング環境での検証が重要です。 IaCでインフラを管理することのメリットは、ステージング環境で検証したことをその通りに本番環境にも適用できることです。 特にCDKでは旧 ドメイン が使われている場所を、コードエディタで簡単に洗い出し、変数として辿れるのが便利だと改めて思いました。 そもそも複数環境を構築するときに同じスタックを使い回す前提で実装することで、実装段階から環境に固有の変数( ドメイン 名が典型的な例ですね)を意識して切り出すような書き方になります。既にそのような書き方がされているCDKアプリにおいて、 ドメイン を変更する作業が簡単に終わったのも当然といえるかもしれません。 CDK最高です。 執筆: @kou.kinyo 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは。 電通 総研テックブログ編集部です。当社は2024年1月に 電通国際情報サービス (ISID)から 電通 総研に商号変更しました。このテックブログ、 はてなブログ 上に構築しているのですが、商号変更にともない実施した ドメイン 移行などについて、本記事で説明します。商号変更なんて頻繁にすることはないと思いますが、 はてなブログ 上で企業技術ブログを運営している方の参考になればと思います。 画像は適当にAIで作成したものです。 スケジュール キックオフ(2023/11) 準備期間(2023/11~12) 切替(2024/1) ふりかえり(2024/1) やったこと サブドメインの取得 はてなブログ側のドメイン設定変更、リダイレクト設定 はてなブログ側のデザイン変更 告知用TwitterアカウントのID、名称変更 動画配信用YouTubeアカウント、ハンドル、チャネル名称変更 運営関連対応いろいろ やらなかったこと 過去の公開記事の文中にある旧社名(ISIDなど)の表記の洗い替え 商号変更日(1/1)のドメイン切替対応 はてなブログのオーナーID変更 トラブルメモ SNS上でのOGP画像が新社名にならない OGP画像のサイズがいまいち ふりかえり スケジュール キックオフ(2023/11) 商号変更の2カ月前に「社名変更チーム」を組成して、ざっくりと作業内容を洗い出して役割分担を行いました。わたしたちはテックブログの運営を業務として実施していますが、外部サービスを最大限に活用して省力化を行っています。利用している外部サービスの設定変更を行えばよさそうです(実際には課題はありました)。テックブログは SNS や各種の情報サイトにURLが連携され、蓄積されています。旧 ドメイン へのリンクは適切に全て転送させることを大方針として、対応を進めることにしました。 また、業務ではありますが重要度を勘案して、実際の商号変更が行われる2024/1/1ではなく当社の業務開始日である2024/1/4に作業を実施することもキックオフの時点で決めています。休日出勤するほどのことではないですしね。 準備期間(2023/11~12) ドメイン 変更のための事前調査、各種サービスへの問合せや、社内の広報部門等との調整を進めました。 切替(2024/1) 後述の対応を、日中にほどほどのスピード感で実施しました(専任体制ではないので、ゆるく実施) ふりかえり(2024/1) この記事のレビューも兼ねて、対応メンバーでふりかえりを実施しました :-) その結果が本記事になります! やったこと サブドメイン の取得 旧 ドメイン (tech.isid.co.jp)も新 ドメイン (tech.dentsusoken.com)も自社管理だったので、担当部署にさくっと依頼して対応してもらいました。他の部署に奪取されるのが嫌なので、申請開始日にさくっと取得しました。早い者勝ちです。 はてなブログ 側の ドメイン 設定変更、リダイレクト設定 旧 ドメイン (tech.isid.co.jp)については SNS を始めいろいろな場所からリンクされているので、これを全て301リダイレクトする必要があります。推進メンバーは全員エンジニアなので「あー、 はてな でリダイレクト設定したら終わりでしょ。楽勝」と考えていましたが、実際には転送機能はありませんでしたw クラウド サービスをうまく活用して手軽にやる方法を模索しましたが、最終的には CARTA さんが公開していた手法を採用することにしました。CARTA TECH BLOGも ドメイン 変更をしたことがあって、現在も以下の手法でリダイレクトしているようでした。情報公開ありがとうございます! 【AWS】CloudFront Functions を使ったリダイレクト機能の実装方法 - CARTA TECH BLOG はてなブログ 側のデザイン変更 はてなブログ の記事以外の要素について、見直しを実施しました。 サムネイル画像、 favicon の入れ替え ヘッダー修正(ブログ名称) フッターおよび各種のリンクのはり替え 社名変更に関する免責文言をフッターに追加 免責文言は、(本記事公開時点で)右下に配置している以下の文章のことです。 2024年1月1日に 電通国際情報サービス ( ISID )は、 電通 総研へ商号変更しました。社名、サービス名、その他の情報は発表当時のものです。あらかじめご了承ください。 過去に執筆された記事の中には旧社名などが記載されています。この修正(洗い替え)は行わず、免責文言を表示することにしました。旧社名は思い出として、ずっと取っておけます。 告知用 Twitter アカウントのID、名称変更 本テックブログでは新規投稿をX(旧 Twitter )に連携して情報発信をしています。アカウント名を「 電通 総研テックブログ」、IDをdentsusokenに変更しました。なお旧アドレスは せどり 防止のため継続して確保しました。 動画配信用 YouTube アカウント、ハンドル、チャネル名称変更 ブログに動画を投稿するための YouTube アカウントも保持していますので、dentsusokenTechBlogに変更しました。 運営関連対応いろいろ 上記までが対外向けに見える対応ですが、社名変更に伴いメールアドレスが変更となるので、内部向けの執筆者向けアカウントの移行(メールアドレス変更対応)や、執筆時に利用しているAI校正ツール「 Shodo 」の社名変更対応等を実施しています。このあたりは、いろいろと取りこぼしもありそうですが、気づいたタイミングで少しずつ是正していく方針でゆるくやっています。 やらなかったこと 過去の公開記事の文中にある旧社名(ISIDなど)の表記の洗い替え 過去記事の文中には旧社名(ISIDなど)が記載されていることもありますが、これは特に修正しない方針としています。 はてなブログ のフッターに免責文言を入れて対応することとしました。面倒くさいしね。 商号変更日(1/1)の ドメイン 切替対応 キックオフの段階でテックブログの ドメイン 移行日は業務開始日の1/4にしたのですが、実際には社内の他サイトからは1/1に切り替えてほしいという声はありました。リダイレクト対応もするので、1/1にしかリンク先を変更できないサイトは旧 ドメイン にリンクしてもらいました。塩対応です。 はてなブログ のオーナーID変更 実はこれは「やらなかった」というより「やれなかった」ことです。 当ブログは はてなブログ 上でISID(旧社名)というユーザーを作成し、オーナーとして開設しています。 このオーナーIDを別アカウントに変更しようと思いましたが、 出来ませんでした …… オーナーを変更したり、メンバーから削除することはできません。 複数人でブログを編集・管理する(ブログメンバー) - はてなブログ ヘルプ ほとんど害がないのですが、実は個別記事のライターとしてこのオーナーIDが表示されてしまっている部分がありました。 このテックブログでは Shodoを使って記事を配信 していますが、配信に使っている はてなブログ のAtomPub API では オーナー以外のブログメンバーのAPIキーでも記事を配信 できるため、新社名の はてなID を取得して記事を配信することにしました。 実はこの記事を書き始めたタイミングではShodoはこの機能に対応していませんでしたが、リク エス トしたらすぐに対応していただけました。おかげさまでこの記事も、新社名のIDで公開できています。( 清原さん 、ありがとうございます!) トラブルメモ というわけで、 はてなブログ 上でテックブログの ドメイン 移行をしたわけですが、ノートラブルというわけではありませんでした。移行後に発生した事をいくつか紹介します。 SNS 上でのOGP画像が新社名にならない 広報:あのーすいません SNS で古い社名が出ちゃってるんですけどー 担当:サムネイル用の画像はちゃんと差し替えたんですけど……あー、OGPキャッシュか! 本ブログは新規投稿時にX(旧 Twitter )に連携していますし、それ以外にも引用されることもあるのですが、その際に表示されるOGP画像が旧社名のままでした。他にも社内で利用しているSharePointPortalや Facebook などでも同様の事象が発生していました。 これは知っている人も多いのですが、各サービスが 保有 しているOGP画像キャッシュが悪さをしているようです。ネット情報を頼りにいろいろやっているうちにキャッシュが書き換わりました。 X(旧 Twitter ):Card Validator を試したけど動かず。でも少ししたらキャッシュが新しくなっていた(謎) Facebook :シェアデバッガーで更新 SharePointPortal:対応するのを忘れているうちに、いつのまにか直ってました OGP画像のサイズがいまいち 広報:あのーすいません SNS で出ている社名のサイズがイマイチなんですけど 担当:えー サムネイルの画像サイズがうまくハマっておらず、周囲の余白(マージン)が足りずに格好悪いということになっていました。が、OGPやブログのサムネイル画像のベストプ ラク ティスはよくわかりません。ネットを検索すると1200px・630pxが良いとされているのですが、うまくハマりませんでした。最終的には630px・630pxのサイズにして周囲に余白ができるようにしました。このあたりは試行錯誤とテストをしたほうが良かったと反省しています。 ふりかえり 以上、 はてなブログ 上で構築したテックブログの ドメイン 移行方法のご紹介でした。準備やリダイレクト対応には少し工夫が必要でしたが、先人の情報を参考にしながら比較的 ラク に実施できたと思います。 引続き、 電通 総研テックブログをよろしくおねがいします。 執筆: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは。XI 本部 AIトランスフォーメーションセンター 所属の山田です。 先日、部内の勉強会でAzure AI Searchについて紹介したので、テックブログでもその内容を紹介したいと思います。 Azure AI Searchとは? Azure AI Searchに保存されるデータに関する用語と概念 Azure AI Searchのスケーラビリティに関する用語と概念 Azure AI Searchでサポートされる検索の仕組み 全文検索について Azure AI Searchのアナライザー ベクトル検索について ベクトルの近傍検索アルゴリズム ハイブリッド検索 検索インデックスのスキーマ設計 検索インデックスにドキュメントを追加する方法 Pushモデル Pushモデルを利用する場合のアーキテクチャの例 Pullモデル Pullモデルを利用する場合のアーキテクチャの例 インデクサーの計算リソース Azure AI Search側からのAzure OpenAI Serviceとの連携 利用されるスキル マッピングされる検索インデックスのスキーマ インデックスプロジェクション Azure OpenAI Service側からのAzure AI Searchとの連携(Add your data) Azure AI Search側に作成されるインデクサー 利用されるスキル マッピングされる検索インデックスのスキーマ まとめ Azure AI Searchとは? Azure AI SearchはAzureのフルマネージドの検索サービスで、以下のような機能をサポートしています。 全文検索 、ベクトル検索、ハイブリッド検索、あいまい検索、自動補完、geo検索など豊富な検索ソリューションに対応 他のAzureサービスとの強力な統合機能を提供 データソースから自動で検索インデックスへの保存処理(インデクサー) OCR やテキストの翻訳などデータソースのコンテンツをAIで解析(スキル、エンリッチメント、ナレッジストア) 最近は、Azure Open AI Serviceと組み合わせたRAGソリューションを実現するための コンポーネント として利用されることが多く、 Microsoft もこのパターンを強く推しています。 本記事では、Azure AI Searchの基本的な用語・概念の紹介からAzure OpenAI Serviceとの連携についてまで紹介します。 Azure AI Searchに保存されるデータに関する用語と概念 Azure AI Searchで扱うデータ構造について理解をするために「検索インデックス」「 スキーマ 」「ドキュメント」「フィールド」という用語・概念を整理しておきます。 用語 概要 検索インデックス 検索のためのデータのコレクション。1つの検索インデックス内に複数のドキュメントが格納される。※単に「インデックス」と表記をする場合もあるが本資料内では「検索インデックス」と表記する。 スキーマ 検索インデックスの構造を定義するもの。 スキーマ は、検索インデックスに格納されるドキュメントにどのようなフィールドが含まれ、それぞれのデータ型など情報を含む。 ドキュメント 検索インデックス内に格納される個々のデータ項目。Azure AI Searchでは JSON 形式で表現される。 フィールド ドキュメント内の個々の属性を示す。 RDB の列に似たもので型が存在する。フィールドによって検索結果の並び替えやフィルタリングが行える。 Azure AI Searchでは、検索インデックスというデータのコレクションを作成し、検索インデックス内に複数のドキュメントを格納する形式でデータを保存します。 Azure AI Searchのスケーラビリティに関する用語と概念 Azure AI Searchのスケーラビリティやコストについて理解するために「SU(検索ユニット、スケールユニット)」「レプリカ」「 パーティション 」「シャード」という用語・概念について整理しておきます。 用語 概要 SU(検索ユニット、スケールユニット) Azure AI Searchサービスの課金単位。SUの数はレプリカと パーティション の数によって決定される。 レプリカ Azure AI Searchでホストされる インスタンス の数。各レプリカは検索インデックスの完全なコピーを持っており、独立でクエリの処理が可能。レプリカ数を増やすことで負荷分散、高可用性を実現できる。 パーティション Azure AI Searchでのストレージの単位。 シャード 検索インデックスを分割した単位。Azure AI Searchでは各検索インデックスは事前にシャードの単位で分割され、 パーティション ごとに均等に分散されて保存される。なおシャードは実装の詳細であるためサービス利用者が意識する必要はない。 検索ユニット(SU)はレプリカと パーティション の数によって決まります。 下図はレプリカ数 1、 パーティション 数 2でのイメージです。この場合、課金単位であるSUは2となります。 ポイントとして可用性に影響を与えるのは「レプリカ数」です。 読み取り/書き取りで99.9%の可用性が必要な場合は3つ以上のレプリカが必要になります。 パーティション は全てのレプリカに適用されるため、レプリカ数の多いリソースで パーティション を増加させるとコスト インパク トが大きいため注意が必要です。 実際、レプリカ数3のリソースで パーティション 数を2に増加した場合にSAの数が6となりコストが大きく跳ねることのイメージがつくかと思います。 また作成可能な検索インデックスの数、ストレージの容量、スケール上限はプランによって決まっているので注意が必要です。 azure.microsoft.com Azure AI Searchでサポートされる検索の仕組み Azure AI Search は「 全文検索 」と「ベクトル検索」のどちらにも対応しています。 全文検索 フルテキストインデックスによって作成された 転置インデックス を利用してドキュメントを検索します。 ベクトル検索 ベクトルの近傍検索によってドキュメントを検索します。これにより検索クエリを直接含んでいない場合にも意味的な類似したドキュメントを抽出できます。 全文検索 について 全文検索 機能を理解するために「 転置インデックス 」と「アナライザー」について整理しておきます。 用語 概要 転置インデックス 文書に含まれる各単語( トーク ン)とその単語が出現する文書IDの組み合わせによって構成される索引(インデックス)。これにより単語が含まれる文書を高速に探し出すことができる。 アナライザー 転置インデックス を構成する際に、文章を単語( トーク ン)の単位に分割する処理機能や コンポーネント を指す。アナライザーでは単語分割( 形態素解析 )やノイズとなる単語( ストップワード )の除去、小文字や原型への変換処理などが行われる。 Azure AI Searchではドキュメントを保存する際に、 転置インデックス の作成・更新処理が行われます。 転置インデックス に格納される単語はアナライザーの種類によって単語分割などの処理が異なるため、利用するアナライザーによって 転置インデックス に格納される単語が変化します。 ユーザーから入力される検索クエリもアナライザーによる単語の分割が行われ、分割した単語を 転置インデックス から探すことでドキュメントの抽出が行われます。 Azure AI Searchのアナライザー 全文検索 機能を利用する上でアナライザーの選択は重要になります。 Azure AI Searchで利用可能なアナライザーは大きく分けると2種類あります。 組み込みアナライザー Azure AI Searchに標準で組み込まれているアナライザーです。組み込みアナライザーには「標準 Lucene のアナライザー」、「言語固有アナライザー」、「特殊アナライザー」があります。言語固有アナライザーは Lucene 言語アナライザーと Microsoft 言語アナライザーがあります。 カスタムアナライザー ユーザー定義のアナライザーです。組み込みアナライザーを拡張するようなこともできます。 アナライザーを特に指定をしない場合「標準 Lucene のアナライザー」が利用され、 転置インデックス が作成されます。 選択するアナライザーによって処理結果がどのように変化するかを少し紹介します。 ここでは組み込みアナライザーの「標準 Lucene のアナライザー(standard)」「 Lucene の日本語アナライザー(ja. lucene )」「 Microsoft の日本語アナライザー(ja. microsoft )」「キーワードアナライザー(keyword)」を使って処理して結果の違いを紹介します。 入力文は以下で試しています。 私は 機械学習 エンジニアです。 アナライザー 分割結果 standard [{'token': '私', 'startOffset': 0, 'endOffset': 1, 'position': 0}, {'token': 'は', 'startOffset': 1, 'endOffset': 2, 'position': 1}, {'token': '機', 'startOffset': 2, 'endOffset': 3, 'position': 2}, {'token': '械', 'startOffset': 3, 'endOffset': 4, 'position': 3}, {'token': '学', 'startOffset': 4, 'endOffset': 5, 'position': 4}, {'token': '習', 'startOffset': 5, 'endOffset': 6, 'position': 5}, {'token': 'エンジニア', 'startOffset': 6, 'endOffset': 11, 'position': 6}, {'token': 'で', 'startOffset': 11, 'endOffset': 12, 'position': 7}, {'token': 'す', 'startOffset': 12, 'endOffset': 13, 'position': 8}] ja.lucene [{'token': '私', 'startOffset': 0, 'endOffset': 1, 'position': 0}, {'token': '機械', 'startOffset': 2, 'endOffset': 4, 'position': 2}, {'token': '学習', 'startOffset': 4, 'endOffset': 6, 'position': 3}, {'token': 'エンジニア', 'startOffset': 6, 'endOffset': 11, 'position': 4}] ja.microsoft [{'token': '私', 'startOffset': 0, 'endOffset': 1, 'position': 0}, {'token': '機械', 'startOffset': 2, 'endOffset': 4, 'position': 2}, {'token': '学習', 'startOffset': 4, 'endOffset': 6, 'position': 3}, {'token': 'エンジニア', 'startOffset': 6, 'endOffset': 11, 'position': 4}, {'token': 'です', 'startOffset': 11, 'endOffset': 13, 'position': 5}] keyword [{'token': '私は機械学習エンジニアです。', 'startOffset': 0, 'endOffset': 14, 'position': 0}] 同一の文に対する処理結果を比較することで、それぞれのアナライザーの特徴を大まかにですが把握できます。 標準 Lucene のアナライザー(standard)は多言語にも対応するため、日本語を扱う際には区切られる単語長が短い傾向にありそうです。 言語アナライザーである Lucene の日本語アナライザー(ja. lucene )の場合は日本語の検索で使いやすいように名詞の抽出が行われています。 同様に言語アナライザーである Microsoft の日本語アナライザー(ja. microsoft )の場合は Lucene の日本語アナライザー(ja. lucene )との違いとしては助動詞「です」が含まれていました。 特殊アナライザーのキーワードアナライザー(keyword)は入力文を区切らずに全体を1つの単語として扱います。 これらの違いは入力する文章によっても異なりますが、選択したアナライザーがどのような特徴を持っているかを把握しておくことは重要です。 ベクトル検索について ベクトル検索機能について理解するには「Embedding(埋め込みベクトル、分散表現)」「vectorizer(ベクタライザー)」「最近傍検索」「近似最近傍検索」について整理しておきます。 用語 概要 Embedding(埋め込みベクトル、分散表現) テキストなどのデータを多次元のベクトルとして表現したもの。意味的に類似性の高いコンテンツはベクトル空間内で互いに近くに位置する。 vectorizer(ベクタライザー) テキストなどembeddingに変換する コンポーネント 最近傍検索 与えられたクエリポイント(ベクトル値)に最も近いデータポイントをベクトル空間から探索する方法。クエリポイントと各データポイントのベクトル間の距離を計算し、最も距離が小さいポイントを特定する必要があるため大規模なデータほど計算コストが必要になる。 近似最近傍検索 与えられたクエリポイント(ベクトル値)に近似的に最も近いデータポイントをベクトル空間から探索する方法。正確さを犠牲にしてパフォーマンスを向上させる方法であり、大規模なデータにも対応できる。 ベクトル検索では事前に文書群をベクトル化し、検索インデックスのドキュメント内のフィールドとして保存しておきます。 ユーザーから入力される検索クエリもベクトル化し、ベクトルの近傍検索によってドキュメントの抽出を行います。 このとき、ユーザーの検索クエリと保存されているベクトルの次元がそろっている必要があります。 なおAzure AI Searchのvectorizer(ベクタライザー)は2024年2月現在パブリックプレビュー版機能であるため注意が必要です。 ベクトルの近傍検索 アルゴリズム Azure AI Searchではベクトルの近傍検索の アルゴリズム として最近傍検索のk近傍法(kNN)と近似最近傍検索のHierarchical Navigable Small Worlds(HNSW)をサポートしています。 デフォルトではHNSWが利用されますが、これはAzure AI Searchを利用する際に保存するデータ量の上限が事前にわからないシナリオが多いためです。 なおパラメータ指定などについては、若本さんの記事が参考になるのでこの部分に興味がある方はぜひご覧ください。 tech.dentsusoken.com ハイブリッド検索 Azure AI Searchでは、 全文検索 とベクトル検索を組み合わせたハイブリッド検索をサポートしています。 ハイブリッド検索は 全文検索 とベクトル検索の結果をReciprocal Rank Fusion (RRF) という アルゴリズム で再ランク付けするもので内部的には 全文検索 とベクトル検索の両方が行われます。 詳細については Microsoft Learn側で紹介されているため興味がある方はご覧ください。 learn.microsoft.com 検索インデックスの スキーマ 設計 実際にAzure AI Searchを使って検索サービスを実現するには、検索インデックスを作成する必要があります。 検索インデックスを作成する際は、検索対象のデータ特性と ユースケース に合わせて スキーマ 設計を行います。 フィールドにはデータ型に加えて、検索時にフィールドがどのように使用されるかを表す属性を定義します。 Azure AI Searchで定義可能な属性は以下になります。 属性 概要 searchable フルテキストインデックスを作成するかを制御する。 true のフィールドには指定されたアナライザーで 転置インデックス が作成される。 filterable $filterクエリで参照できるかを制御する。 sortable ソート対象に利用できるかを制御する。 facetable 検索結果の集約化に利用するかを制御する。 key ドキュメントの一意識別子となるフィールド。このフィールドは文字列(Edm.string)で定義される。 retrievable 検索結果に含めるかを制御する。 false にしたフィールドはスコアリングの内部ロジックなどに応用できる。 スキーマ 設計時は ユースケース に応じてフィールドの属性を設定することが重要です。 リッチな スキーマ を定義することで強力な検索機能を実現できますが、より多くのストレージを利用することになります。 特に検索可能(searchable)なフィールドは必要なものだけを定義し、無駄な 転置インデックス が作成されるのを防ぎましょう。 フィルタ可能なフィールドやソート可能なフィールドもインデックスサイズに影響を及ぼします。 インデックスのサイズと検索パフォーマンスには トレードオフ があることを理解しておくことが重要です。 検索インデックスにドキュメントを追加する方法 Azure AI Searchでは、検索インデックスに対してドキュメントを追加する方法としてPushモデルとPullモデルの二種類があります。 Pushモデル Pushモデルは、Azure AI SearchのAzure SDK や REST API を利用して、検索インデックスに対して JSON 形式のドキュメントを追加する方法です。 PushモデルはAzure SDK /HTTPリク エス トをサポートする任意の プログラミング言語 やモジュールで検索インデックスへのドキュメントの追加処理を記述可能です。 Pushモデルを利用する場合の アーキテクチャ の例 Pushモデルでは任意の プログラミング言語 で処理の記述が行えるため、Azure AI Searchへのドキュメント追加方法について様々なアプローチを取ることができます。 以下の アーキテクチャ イメージは、ファイルからテキストデータの抽出、Azuree AI Searchへのドキュメント追加をAzure App Serviceのアプリケーションで行う場合になります。 RAG と生成 AI - Azure AI Search | Microsoft Learn その他にも、Azure Functionsを用いてイベント駆動型の アーキテクチャ でAzure AI Searchにドキュメント追加を行う場合の アーキテクチャ イメージもあります。 Azure でドキュメントの分類を自動化する - Azure Architecture Center | Microsoft Learn Pullモデル Pullモデルは、Azure AI Searchがサポートしているデータソースをクロールし、自動で検索インデックスにドキュメントを追加する方法です。 Pullモデルの仕組みや機能を理解するために「データソース」「インデクサー」「スキル」「スキルセット」「エンリッチメント」「ナレッジストア」という用語・概念を整理しておきます。 用語 概要 データソース インデクサーのデータ抽出対象となる クラウド 上のデータソース。Azure Blob StorageやAzure Cosmos DBなどがサポートされる。 インデクサー インデクサーはデータソースのデータを検索インデックスの スキーマ 構造に マッピング する処理を行う コンポーネント 。一般的な検索システムの「 クローラー 」のような処理を担う。 スキル インデクサーでコンテンツを検索インデックスに投入する際に、コンテンツを変換する単一の操作を提供するもの。 スキルセット スキルセットは特定のインデクサーで利用するスキルの集合。少なくとも1つのスキルから構成され、最大で30のスキルを含む。 エンリッチメント インデクサーの 拡張機能 で、画像などのテキスト情報を持たないデータを検索可能な構造に変換するもの。 ナレッジストア エンリッチメントされたコンテンツを保存するストレージ。Azure Blob StorageやAzure Table Storageを利用できる。 Pullモデルを使って検索インデックスにドキュメントを追加する場合は、インデクサーを使って検索インデックスに対してドキュメントを追加します。 インデクサーの作成手順としてはデータソース、スキルセットを作成し、それらを利用するインデクサーを作成するという流れになります。 Pullモデルを利用する場合の アーキテクチャ の例 Pullモデルでは、Azure AI Searchの検索インデックスへのドキュメントの追加処理はAzure AI Searchに閉じた形になります。 以下の アーキテクチャ イメージは、ストレージアカウントをデータソースに登録して自動で検索インデックスにドキュメントを追加する場合になります。 スキルセットを利用することで、抽出したテキストデータをTranslatorで他言語に翻訳することやAzure AI Document Intelligenceと組み合わせたドキュメント分析なども可能です。 RAG と生成 AI - Azure AI Search | Microsoft Learn Pullモデルのインデクサーでは マッピング 対象の検索インデックスを1つだけ指定する必要がありますが、複数のデータソースを1つの検索インデックスに マッピング できます。 以下の アーキテクチャ イメージは別のデータソースとして登録されたBlobストレージとTableストレージに対し、個別のインデクサーを定義し、1つの検索インデックスに マッピング をしている例です。 Azure AI 検索を使用してファイルの内容とメタデータをインデックスする - Azure Architecture Center | Microsoft Learn インデクサーの計算リソース Pullモデルでのインデクサーを使ってのドキュメントの追加処理はAzure AI Search側で行われます。 この実行環境には「プライベート実行環境」と「マルチテナント実行環境」の2つが存在します。 環境 概要 プライベート実行環境 リソース固有の環境。ここで実行されるインデクサージョブは最大24時間実行可能。プライベート環境で実行可能なインデクサージョブの数は検索ユニットで1つ。プライベートエンドポイント経由で他のリソースにアクセスする必要があるインデクサージョブはこの環境で実行する必要がある。 マルチテナント実行環境 マネージドな環境。ここで実行されるインデクサージョブは最大2時間実行可能。実行できるインデクサージョブの数は不確定。 インデクサーがネットワークで保護されたリソースにアクセスする場合は、この実行環境の違いについて把握しておく必要があります。特にプライベートエンドポイントでリソースにアクセスする必要がある場合はプライベート実行環境でインデクサーが実行されるように追加で構成をする必要があります。 Azure AI Search側からのAzure OpenAI Serviceとの連携 Azure AI SearchからAzure OpenAI Serviceリソースを連携させ、Blobストレージのコンテンツをベクトル化し、検索インデックスにドキュメントに追加するインデクサーを構成できます。 この操作はAzureポータルの「データのインポートと ベクター 化」から実施できます。 「データへの接続」ではBlobストレージの情報を入力します。 「データの ベクター 化と強化」ではAzure OpenAI Serviceリソースの情報を入力します。 画面キャプチャ上では「毎時間」となっていますが、インデックス作成のスケジュールを「即時」にすると操作終了後にインデクサーが実行できます。 この操作の裏では、Pullモデルの節で紹介したインデクサーの作成と同じことが行われておりデータソース、スキルセット、インデクサーのリソースがAzure AI Search内に作成されています。 利用されるスキル 作成されるインデクサーのスキルセットには「テキスト分割スキル」と「Azure OpenAI Embedding スキル(プレビュー)」の2つのスキルが含まれています。 テキスト分割スキルはBlobストレージのコンテンツから抽出したテキストデータをいわゆる「チャンク」という単位に分割するスキルになります。 learn.microsoft.com テキスト分割スキルの定義の JSON 情報(一部抜粋)を確認すると、チャンクに含まれる最大文字列長は2,000、オーバラップサイズは100で構成されているようです。 { " @odata.type ": " #Microsoft.Skills.Text.SplitSkill ", " name ": " #1 ", " description ": null , " context ": " /document ", " defaultLanguageCode ": " en ", " textSplitMode ": " pages ", " maximumPageLength ": 2000 , " pageOverlapLength ": 100 , " maximumPagesToTake ": 1 } Azure OpenAI Embeddingスキルはチャンクに区切られた各テキストをEmbeddingモデルの「text-embedding-ada-002」を使ってベクトル化するスキルになります。 learn.microsoft.com Azure OpenAI Embeddingスキルの定義の JSON 情報(一部抜粋)を確認すると「emebedding-ada-002」モデルが利用されていることがわかります。 { " @odata.type ": " #Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill ", " name ": " #2 ", " description ": " Azure OpenAI Embedding Skill ", " context ": " /document/pages/* ", " resourceUri ": " https://*****.openai.azure.com ", " apiKey ": " ***** ", " deploymentId ": " emebedding-ada-002 ", " authIdentity ": null } マッピング される検索インデックスの スキーマ マッピング 対象の検索インデックスは選択できず自動で作成されます。そのため マッピング 対象の スキーマ には制限があります。 以下は作成される検索インデックスの スキーマ になります。 インデックスプロジェクション この操作で構築されるインデクサーは内部でインデックスプロダクションという機能を利用しています(2024年1月時点で プレビュー機能 )。 これは通常のインデクサーが1ファイルを1つのドキュメントに マッピング するのに対し、1ファイルを複数のドキュメントに マッピング させる機能です。 これはチャンク分割、ベクトル化の処理で1対多の マッピング が必要なためです。 learn.microsoft.com Azure OpenAI Service側からのAzure AI Searchとの連携(Add your data) 続いてAzure OpenAI Service側からAzure AI Searchと連携する操作を試してみます。この機能は「Add your data」とも呼ばれています。 Azure AI Studioのチャットプレイグラウンドの「データの追加(プレビュー)」の「データソースの追加」より操作可能です。 「データの追加」では対象のデータソース、データを保存するAzure AI Searchリソース、検索インデックス名の指定などをします。ここでAzure AI Searchの「インデクサー」という単語が登場しています。 またベクトル検索を有効にするかの チェックボックス が存在します。 「データ管理」ではAzure AI Searchで行う検索の種類、チャンクサイズを指定します。 構成が完了するとAzure OpenAI ServiceのチャットプレイグラウンドでAzure AI Searchをデータソースとして回答生成させることができます。 Azure AI Search側に作成されるインデクサー Azure OpenAI Service側から連携した場合も、Azure AI Searchのインデクサーが利用されます。 そのためAzure AI Searchにデータソース、スキルセット、インデクサーが作成されます。 インデクサーの処理で重要となる、スキルセット内で利用されるスキルについて紹介します。 利用されるスキル Azure OpenAI Serviceから連携した場合に作成されるインデクサーでは「カスタム Web API 」スキルが利用されます。 learn.microsoft.com 以下はスキルの定義の JSON 情報(一部抜粋、パラメータマスク済み)です。 { " @odata.type ": " #Microsoft.Skills.Custom.WebApiSkill ", " name ": " {スキルセット名} ", " description ": null , " context ": " /document/content ", " uri ": " https://******.openai.azure.com/openai/preprocessing-jobs?api-version=2023-03-31-preview ", " httpMethod ": " POST ", " timeout ": " PT3M ", " batchSize ": 10 , " degreeOfParallelism ": 10 , " inputs ": [] , " outputs ": [] , " httpHeaders ": { " original-request-id ": " {検索インデックス名} ", " num-tokens ": " 1024 ", " embedding-deployment-name ": " {ベクトル化に利用するモデルのデプロイ名} ", " api-key ": " {Azure OpenAI ServiceのAPIキー} ", " connection-string ": " {Blobストレージへの接続文字列} ", " container-name ": " {Blobストレージのコンテナ名} " } } カスタムWeb API はAzure OpenAI Serviceの preprocessing-jobs というエンドポイントを呼び出しており、チャンク分割の トーク ン数はHTTPヘッダーの num-tokens で送信していることがわかります。 なお preprocessing-jobs の詳細な情報は公開されている情報を見つけることができませんでした。 ここまででわかったことは、Azure OpenAI Service側からAzure AI Searchと連携する場合とAzure AI Search側からAuzre OpenAI Serviceと連携する場合で似たパラメータを設定しますが内部で利用されるスキルなどは異なっているということです。 マッピング される検索インデックスの スキーマ マッピング される検索インデックスの スキーマ は以下のようになっています。 検索インデックスの スキーマ もAzure OpenAI Service側からAzure AI Searchと連携する場合とAzure AI Search側からAuzre OpenAI Serviceと連携する場合で異なります。 こちらの検索インデックスの スキーマ も自動で作成されるため、 マッピング 対象の スキーマ には制限があります。 まとめ 本記事ではAzure AI Serchを理解するために基本的な用語や概念の説明からAzure OpenAI Serviceとの連携部分まで紹介しました。 Azure OpenAI Serviceとの連携では、Azure AI Serch側から連携する場合とAzure OpenAI Service側から連携する場合のどちらもインデクサーを利用しますが、その処理内容に違いがあることを取り上げました。 処理の違いについては、時系列的にAzure OpenAI Service側から「Add your data」によるAzure AI Serchとの連携機能の方が先に公開されていた影響が大きいかと個人的には考えています。 なおどちらの場合も、現在(2024年2月)はプレビュー版の機能が使われており、プロダクションへの適用は難しい状態です。 しかしながらスキルなどのパラメータは十分カスタマイズ可能なものが用意されているので、今後、 SDK などからの操作がサポートされると安定して使えそうなビジョンがあります。 本記事の内容が役に立てれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研中途採用ページ 電通総研新卒採用ページ 執筆: @yamada.y 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
初めに 電通 総研 X(クロス) イノベーション 本部 の三浦です。 プライベートではChatGPT4を積極的に利用しておりますが、いくつかの不満点も抱えておりました。そんな中、ローカル環境で高性能なモデル「Orion-14B」がリリースされたと聞き、試しに使ってみることにしました。 なお、最初に結論を書いてしまいますが、「Orion-14B」を使用しても「ChatGPT4」で感じていた不満は解消されなく、「ChatGPT4」の強さを思い知らされた・・といった感じです。 しかし、個人でも最新モデルを容易に試せるという点で、今回の試行内容をご紹介いたします。 Orion-14Bとは? ChatGPT4様による解説 Orion-14BはOrionStarAIによって開発された大規模な多言語対応の言語モデルシリーズです。このモデルは、14億のパラメータを持つ基礎モデルと、それに関連するいくつかの特化モデルから構成されています。これらのモデルには、チャットモデル、長文テキストモデル、量子化モデル、RAG微調整モデル、エージェント微調整モデルなどが含まれます。 Orion-14Bの基礎モデルは、2.5兆トークンの多様なコーパス上でトレーニングされ、そのデータは英語、中国語、日本語、韓国語などのテキストから収集されています。このモデルは、様々な言語で優れた性能を示しています。また、Orion-14Bには会話アプリケーションやその他の特定の用途に合わせて微調整されたモデルシリーズも含まれています。評価結果によると、Orion-14Bは多くのタスクで最先端の性能を達成しているとされています。 参考リンク https://github.com/OrionStarAI/Orion https://huggingface.co/OrionStarAI/Orion-14B-LongChat 下準備 Google Colabを契約する 無償で使用できる範囲では利用できないため、まず契約からとなります。 が、そこまでの Google Colabヘビーユーザーではないため、 「Pay As You Go」「100 Colab コンピューティング ユニット¥1,072」 を購入して今回は試しました。 月額契約をしなくても使いたい時だけ Google Colabを使えるのは非常に助かりますね。 https://colab.research.google.com/signup/pricing?hl=ja 初期化部分 最高レベルの性能がどれくらい出るか知りたいため、 量子化 モデルでは試さずに一番性能がよさそうな? 「Orion-14B-LongChat」で試行します。 そのためには、大量のリソースが必要となるため、 Google Colabのランタイムは、「A100 GPU 」を選択します。 A100 インスタンス につなげたら、下のノートブックを実行します。 モデルのダウンロードにかなりの時間がかかります。 !pip install -U transformers accelerate bitsandbytes !pip install -U sentencepiece flash_attn autoawq !pip install -U torch-sdp import torch from transformers import AutoModelForCausalLM, AutoTokenizer from transformers.generation.utils import GenerationConfig tokenizer = AutoTokenizer.from_pretrained( "OrionStarAI/Orion-14B-LongChat", use_fast=False, trust_remote_code=True ) model = AutoModelForCausalLM.from_pretrained( "OrionStarAI/Orion-14B-LongChat", device_map="auto", torch_dtype=torch.bfloat16, trust_remote_code=True ) model.generation_config = GenerationConfig.from_pretrained( "OrionStarAI/Orion-14B-LongChat" ) 質問編 こんな単純なコードで使用できます。 messages = [ {"role": "user", "content": """ logstashのメモリ設定状況をしりたいです。 curlで調べる方法をおしえてください。9600で調べるやつありましたよね? """} ] response = model.chat(tokenizer, messages, streaming=False) print(response) 返ってきた答えはこちら はい、curlでログを表示させる方法があります。 curl -i http://localhost:9600/_cat/var?v=true このコマンドを実行すると、サーバーのメモリ使用量が表示されます。 また、`-i`オプションを指定することで、ヘッダ情報も表示されます。 curl -i http://localhost:9600/_cat/var?v=true&h=memory このコマンドを実行すると、サーバーのメモリ使用量とヘッダ情報が表示されます。 以上です。 結果は残念ながら、明らかな誤りが含まれています。 これはChatGPT4による回答で、非常に有用な内容です。 ぱっといろんなことが思い出せなくなってきている中年エンジニアにはとてもありがたいです。 もっと簡単なことも頼んでみましょう。 ローカルのファイル名一覧(拡張子無し)を作ってお願いします。 # メッセージリストの準備 messages = [ {"role": "user", "content": """powershellで、特定フォルダのファイル名一覧をください。拡張子は抜いてください"""} ] # 推論の実行 response = model.chat(tokenizer, messages, streaming=False) print(response) それっぽい結果が出ました。 Get-ChildItem -Path "C:\test" | Select-Object -Property Name, FullName | Where-Object {$_.FullName -ne $null} | ForEach-Object {$_.Name -replace '\.',''} が、実際にこれを実行してみると拡張子を取り除くという要件が満たせておらず、ただ拡張子前の「.」を取り除くだけのコードとなっています。 ChatGPT4様の場合 powershellで、特定フォルダのファイル名一覧をください 拡張子は抜いてください えっと、jsonにするパターンも 簡潔な質問にも丁寧に回答しています。 まとめ ChatGPT4を上回るローカルLLM(Large Language Model)が存在すれば大いに助かるという期待を持ち、今回の試用に臨みましたが、残念ながら期待した成果は得られませんでした。しかし、この分野の進歩は著しく速いため、今後も定期的に有望なモデルを探し、その可能性を探っていきたいと思います。 執筆: @miura.toshihiko 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
初めに ISID X(クロス) イノベーション 本部 の三浦です。 日に日に生成AIへの依存を深める日々を過ごしています。 今回は、OpenAIの API (デフォルトモデルはgpt-3.5-turbo-16kです)を利用したOpenCommitという OSS を使用しましたのでご紹介いたします。 コミットメッセージ生成ツールとなります。 OpenCommitとは? 公式ページ https://github.com/di-sukharev/opencommit 実行イメージ ChatGPT4による特徴解説 主な特徴は以下のとおりです: AI駆動: OpenCommitは、GPT-3.5ターボまたはGPT-4を使用してコミットメッセージを生成します。 カスタマイズ可能: ユーザーは絵文字の使用やコミット説明の長さをカスタマイズできます。 簡単な使用法: OpenCommitは、ステージングされた変更に対してコミットメッセージを生成するために直接呼びだすことができます。使用法はgit add <files...> に続いて opencommit または oco を実行するだけです。また、GitHub actionで呼びだすことも可能です。 ローカルおよびグローバル設定: .env ファイルを作成してOpenCommit設定変数を追加することで、リポジトリごとのローカル設定を行うことができます。また、oco config set コマンドを使用してグローバル設定も可能です。 OpenCommitは、コーディングプロセスを簡素化し、コミットメッセージの品質を向上させることを目的としています。詳細は、GitHubのOpenCommitリポジトリやGitHub Marketplaceのページで確認できます。 今回の目的、背景 コミットメッセージの自動生成には、以下の方法が考えられます(他の OSS もありますがその辺は割愛します)。 GitHub Copilotで生成する OpenCommitで開発者の端末でコミットメッセージを生成する OpenCommitで GitHub Actions上で生成する 今回の主な目的は、適切なコミットメッセージを記入しない開発者への牽制と、ブラウザを介して修正を行うメンバーへの対応です。そのため、開発者の手間がかからない 「OpenCommitで GitHub Actions上で生成する」 を進めます。 なお、この機能は現在β版として提供されているため、使用する際は各自の責任で十分に検証した上でご利用ください。 Github Actionの設定 https://github.com/di-sukharev/opencommit?tab=readme-ov-file#setup-opencommit-as-a-github-action-beta- 設定プロセスは非常に簡単です。指定されたリンク先のファイルを作成するだけで済みます。 基本的な設定を行い、次に動作に必要なOCO_OPENAI_ API _KEYを設定します(OpenAIの API キーです。OpenCommit用にキーを発行しておくとよいでしょう)。 現時点で、OpenCommitはOpenAIの API のみに対応しており、AzureのOpenAIモデルはサポートされていません。 この設定で基本的な機能は動作しますが、さらに使いやすくするために私の環境では以下のような改変を加えました。 上記コミットに対して自動生成されたコミットメッセージが下記です。 OpenCommitが生成したコミットメッセージを確認してみましょう 🔧 修正(OpenCommit.yml): OpenCommitワークフローのカスタマイズ設定を変更 ✨ 新機能(OpenCommit.yml): OCO_DESCRIPTIONとOCO_EMOJIを有効化し、OCO_LANGUAGEをjaに変更 OpenCommitワークフローのカスタマイズ設定を変更しました。OCO_DESCRIPTIONとOCO_EMOJIをtrueに設定することで、コミットメッセージに説明と絵文字を追加するようになりました。また、OCO_LANGUAGEをjaに変更することで、コミットメッセージを日本語で生成するようになりました。これにより、より詳細なコミットメッセージが生成され、開発者間のコミュニケーションが向上しました。 おお、いい感じですね!! OpenAIの API 利用費用の確認 OpenAIのプラットフォーム( https://platform.openai.com/usage )から、使用状況を確認することができます。このように、費用のリアルタイムな把握、 API キーごとの利用状況が一覧できるため、安心して利用できますね。 まとめ この記事では、コストを抑えつつコミットメッセージを改善する方法をご紹介しました。私自身、簡単な作業を頻繁に行い、ブラウザを通じて修正を加えることが多いのですが、ブラウザを介してもコミットメッセージのアシストが得られるのは非常に助かります。 執筆: @miura.toshihiko 、レビュー: @fukutake.hiroaki ( Shodo で執筆されました )
アバター
こんにちは。 電通 総研 金融ソリューション事業部の若本です。 今回は、RAG内部で使用するベクトル検索の設定のチューニングについての記事になります。 RAGとは ベクトル検索になぜチューニングが必要なのか? 近似近傍探索 実験 検索対象 検索クエリ 評価方法 結果 おわりに RAGとは RAG(Retrieval-Augmented Generation)は、ユーザーのクエリに対して外部の知識を検索し、それをもとにChatGPTのような大規模 言語モデル (LLM: Large Language Model)に回答を生成させる手法になります。 具体的な方法は 山下さんの記事 でも紹介されていますので、興味のある方はぜひご一読ください。 ベクトル検索になぜチューニングが必要なのか? RAGの検索において、よく使われる検索方法の1つがベクトル検索です。 ベクトル検索では文章を「ベクトル」という表現に変換して検索を行います。意味合いが似ている文章を、使われる単語が異なっていても検索できるため、非常に重宝される技術です。 しかし、「ベクトル」という複雑な表現同士で検索するには工夫が必要です。 例えば、langchainのfaissの Vector storeではデフォルト設定が L2 normを用いた総当たりのベクトル検索(IndexFlatL2) になっています。 総当たりのベクトル検索では、ユーザーのクエリのベクトルとDBに保持しているベクトルを1件ずつ比較する必要があるため、 DBに保持しているベクトル数が多いほど検索に時間がかかってしまい(O(n))、プロダクション向きではありません。 そこで、より高速なベクトル検索として近似近傍探索(ANN: Approximate nearest neighbor search)という技術があります。 近似近傍探索 近似近傍探索では、様々なアプローチで高速なベクトル検索の実装が試みられています。近似近傍探索の種類については、 ANNベンチマーク )などを参照ください。 しかし、近似近傍探索も万能ではなく、速度が総当たりのベクトル検索より早いぶん 総当たりと同じ検索結果を取得できない場合があります。 検索結果をもとに回答を行うRAGの仕組みでは、本来取得できたはずの検索結果が抜け落ちてしまうことは性能の悪化に繋がります。 近似近傍探索の有名な手法の1つであるHNSWについて実装し、RAGを想定したパフォーマンスのチューニングを試みます。 例えば、 Azure AI Searchでも近似近傍探索としてHNSWが使用されています 。 HNSW(Hierarchical Navigable Small Worlds)は、ベクトル同士が連結されたグラフを順番に辿ることで、最も似ているベクトルを探策する手法です。詳細な理論については、 Pineconeの記事 などにわかりやすくまとめられています。 HNSWには3つのパラメータがあり、それぞれ増やすメリット/デメリットが存在します。 M: 接続できるノードの数を表す 増やすと検索精度が上がるが、検索時間と消費メモリも増加する efConstruction: インデックスの構築時に探索されるエントリポイント数 増やすと検索精度が上がるが、検索時間※とインデックス構築時間も増加する efSearch: 検索中にレイヤー間で探索されるエントリポイント数 増やすと検索精度が上がるが、検索時間が増加する ※Mが高い場合のみ 参考までに、Azure AI SearchにおけるHNSWのデフォルトのパラメータはmが低く、その他のパラメータが高く設定されています( 引用元 )。 今回は、上記のパラメータを ベンチマーク として、HNSWを用いたベクトル検索でRAGを行うためのパラメータ探索を行います。 実験 検索対象とクエリについてデータを用意し、ベクトル検索の実行と評価を行いました。 検索対象 検索対象として、様々なトピックの Powerpoint ファイルを120件用意しました。 上記のファイルをlang chainのUnstructuredPowerPointLoaderで読み込み、Text splitterで分割しています。 なお、今回のText splitterの設定は下記としました。 chunk size(分割した文章単位の文字数): 1000 overlap size(文章のオーバーラップ): 500 作成後のindexのサイズは1069となりました。 また、ベクトル検索に使用するembeddingはOpen AI API のtext-embedding-ada-002を使用しました。ada-002の次元数(ベクトルのサイズ)は1536です。 検索クエリ 今回はRAGで使用することを想定したチューニングのため、実際のRAGでユーザーが入力しそうなクエリをGPT-4で大量に生成しました。生成した件数は200件です。 評価方法 今回評価する内容は2つです。 一致率(%) クエリでベクトル検索を行い、 ヒットした結果のセットが総当たり検索の結果セットと完全一致するかを確認します (単一の正解を定めてRecallを取っているわけではない)。今回は検索数を10としました。 このとき、結果セットの順序変動はRAGへの影響が(実際にはありますが)少ないとみて無視します。 1件当たりの検索時間(s) バッチ的に処理した検索時間を件数で割ったもの。 faissに実装されているHNSW を用いて、M、efSearch、efConstructionのパラメータを探索しながら上記の評価を行います。 その他、本来であればインデックス作成時間なども計測すべきところですが、今回は対象外としました。 結果 M、efSearch、efConstructionを変えたときのHNSWの評価結果は下記のようになりました。 まずは一致率です。Y軸が一致率(%)を、X軸がefSearchを表しています。Mを変えた場合はラベルで、efConstructionを変えた場合はサブプロットで表現しています。 同様に、Y軸を検索時間(s)に変えて評価結果を集計しました。 ここで、Azure AI Searchで使用されているデフォルトのHNSWパラメータでは、今回の実験設定において5.5%のクエリで検索結果が異なる結果となっていました。検索速度は1件当たり約0.25ミリ秒を計測しています。 ※今回はローカル上のfaiss実装(CPU)で実験を行っています。そのため実際のAzure AI Searchでの検索結果/検索速度ではないことにご注意ください。 実験の結果より、M、efSearch、efConstructionを抑えつつ、一致率を100%にする設定は下記となりました。 検索速度についても1件当たり約0.1ミリ秒となっており、上記の設定よりも高速に検索できています。 精度と検索速度が上がった一方、メモリの消費量は増加しました。HNSWにおける1件当たりのメモリ消費量は以下のとおりです( 引用 )。 4 × d + x × M × 2 × 4 ※d=ベクトルの次元数、x=インデックスの数 インデックスのメモリサイズを試算すると、デフォルトのHNSWパラメータでは43MB(1件あたり約40KB)、一致率100%の設定では79MB(1件あたり約75KB)となりました。精度と検索速度が改善した代わりに、インデックスのメモリサイズが約2倍に増加しています。 そこで、メモリの増加を防ぐためにMについてより詳細に探索します。Mを4から8の間に設定し、再度検証を行いました。 上記より、M=5のとき一致率が100%となる設定を発見できました。検索速度もデフォルトパラメータよりよい結果が得られています。 インデックスのメモリサイズについても52MB(1件あたり49KB)となり、ある程度抑えることができているため、今回の設定では上記のパラメータで検索インデックスを作成するのがよさそうです。 また余談ですが、今回は8-bit PQ(Product Quantization)を組み合わせたチューニングも実施しましたが、今回の実験設定では効果が出ない結果となりました。 おわりに 次元数の多いembeddingでのHNSWパラメータの探索や、RAG向けのベクトル検索についての技術記事があまり見当たらなかったため、今回は独自で検証を行いました。 結果として、今回はより一致率や検索時間に優れたパラメータを探索することができました。ベクトル検索のチューニングにおいては精度・検索速度・メモリの トレードオフ となるため、提供するRAGシステムの問題設定(ファイル数、文章 ドメイン 、RAGの仕様)やサービス設計などに合わせて同様の検証が求められます。 今回はHNSWに閉じた検証となりましたが、用途に応じて他の近似近傍探索手法を採用することも考えると非常に奥が深いですね。 この記事がどなたかの役に立てれば幸いです。ここまでお読みいただきありがとうございました。 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 以前 Amazon Verified Permissions についての 記事を書きました が、アプリケーションの認可以外にもこのサービスを利用できるのではないかと思い、汎用のポリシー判定エンジンとして使ってみました。具体的な ユースケース でお話ししたいと思います。 社内クラウド SOC 運用上の課題 Verified Permissions を汎用ポリシー判定エンジンとして使う Cedar ポリシー スキーマ定義 ポリシーの例1 ポリシーの例2 評価結果の例 SDK で Verified Permission を呼びだす例 Verified Permissions のサービスクオータ まとめ 社内 クラウド SOC 運用上の課題 Verified Permissions の話に繋げる前に、まずは普段の業務で発生している具体的な課題についてお話します。 電通 総研では クラウド 環境を組織的にセキュアに保つために、 クラウド SOC による複数案件アカウントの集中監視を行っております。 こちらのオペレーションの一つに、 案件アカウントが GuardDuty アラートを検知した場合、 クラウド SOC でも内容を確認して案件担当に確認を依頼する というものがあります。(以前のブログ「 Security Hub の社内展開を支えるツールたち 」でもご説明しています。) ただし、GuardDuty がアラートとして検知した挙動が、案件アカウントの正当な挙動であって実際には問題がない場合が多くあります。 正当な挙動が繰り返されれば、そのうち GuardDuty が学習してアラートを出さなくなることもありますが、頻繁に行われない操作などの場合はいつまでも GuardDuty にアラートとして扱われる場合もありました(GuardDuty のアラート検知ロジックは ブラックボックス なので、どうすればアラート扱いされなくなるかは分かりません)。このように「正当」と分かりきっている挙動の場合は、 煩わしさを解消するために クラウド SOC から案件担当への連絡をスキップしたい ことがあります。 例として、案件 A では以下のような条件のアラートは正当な操作なので、 クラウド SOC からの確認依頼はスキップする運用としたいです。(以下に当てはまらないアラートは、通常通り確認依頼を行いたいです) <通知スキップの条件> ・アカウントID: 111122223333 ・GuardDutyの検出タイプ: TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver ・対象EC2インスタンス: arn:aws:ec2:ap-northeast-1:111122223333:instance/i-0123456789 ・リモートIPアドレス: 1.2.3.4/32 または 1.2.3.5/32 また案件 B では、以下のような条件のアラートは正当な操作なので、確認依頼はスキップする運用としたいです。 <通知スキップの条件> ・アカウントID: 444455556666 ・GuardDutyの検出タイプ: TTPs/Exfiltration:S3-AnomalousBehavior ・操作対象のS3バケット名: 「my-log-bucket-」から始まるバケット名 ・発生時間帯: 毎月1日の 2:00AM ~ 6:00AM 案件ごとに異なる条件が多数ある状況で、アラートがスキップ対象かどうかをいかに素早く、正確に判定するかが課題となっていました。条件が増えてくると人手で突き合わせるのは間違えやすいですし、時間がかかってしまいます。GuardDuty のアラートを受け取る Lambda 関数にロジックを書いてスキップ判断をすることがまず思いつくやり方ですが、 条件によってスキップと判定するためのフィールドが全く異なる上、AND 条件と OR 条件が入り混じっていたり、文字列の部分一致条件があったり、時間帯のように値の範囲による判定条件があったり 、判定ロジックの実装や判定条件データの保持方法には工夫が必要だと感じました。 そこで Verified Permissions が登場します。Verified Permissions を汎用的なポリシー判定エンジンとして利用すれば、判定ロジックの実装の手間を省けるのではないかと考えました。 ※ AWS は Verified Permissions を認可サービスとして打ち出している現状、このような使い方を推奨しているわけではありません。実験的に「このように使ってみた」という記事としてご認識ください。 ※汎用的なポリシー判定エンジンとしては OSS の Open Policy Agent (OPA) が有名ですね。以前の柴田さんの記事で詳しく説明されています。 Policy as Codeを実現する Open Policy Agent / Rego の紹介 OPA に対して、 AWS マネージドサービスである Verified Permissions はポリシーを作成するだけで使えるのが便利です。 Verified Permissions を汎用ポリシー判定エンジンとして使う 以前の記事 で記載した通り、 Verified Permissions は特定条件における認可結果を ALLOW / DENY として返してくれますが、その後の扱いは完全にアプリケーションの責務となります。逆に言うとアプリケーション側に自由度があり、 ALLOW / DENY は好きなように解釈して良いわけです。そこで ALLOW は「アラート通知スキップ」、 DENY はスキップせずに「通知」、として扱う ことを考えます。 そして Verified Permissions に登録するポリシーを「アラート通知をスキップする条件(およびロジック)」として扱う ことで、条件の判定を完全に Verified Permissions にお任せし、その結果を便利に利用することができます。 Cedar ポリシー 以上の ユースケース を実現するための Cedar 言語による Verified Permissions のポリシー例を書いてみます。実際に使う場合は、 公式サイトのチュートリアル などで Cedar 言語の動きを一通り理解しておくことをおすすめします。 Verified Permissions のポリシーにはアプリケーションの認可を想定したマネージドサービスなので、 principal 、 action 、 resource をポリシーの中で明示できます。ただし今回は任意のフィールドで判定をさせたいため、 principal 、 action 、 resource は全許可し、代わりに context で全て判定させてみます。 スキーマ 定義 スキーマ 定義は次のようにしました。 { " GuardDutyApp ": { " commonTypes ": { " ContextType ": { " type ": " Record ", " attributes ": { " accountId ": { " type ": " String ", " required ": false } , " findingType ": { " type ": " String ", " required ": false } , " ec2InstanceArn ": { " type ": " String ", " required ": false } , " s3BucketName ": { " type ": " String ", " required ": false } , " remoteIpAddress ": { " type ": " String ", " required ": false } , " date ": { " type ": " Long ", " required ": false } , " hour ": { " type ": " Long ", " required ": false } , " minute ": { " type ": " Long ", " required ": false } } } } , " entityTypes ": {} , " actions ": { " check ": { " appliesTo ": { " context ": { " type ": " ContextType " } } } } } } commonTypes に context で利用する属性を記載しています( accountId 、 findingType 、 ec2InstanceArn 、 s3BucketName 、 remoteIpAddress 、 date 、 hour 、 minute )。スキップ判定に利用する属性が増えたら適宜追加していきます。 check というアクションを定義し、定義した context を利用するように設定しています。今回は Verified Permission の呼び出しでは常に check というアクションを利用するようにします。 今回は principal と resource は利用しないので、 entityTypes は空にしています。 ポリシーの例1 まずは上で述べた案件 A の判定条件: <通知スキップの条件> ・アカウントID: 111122223333 ・GuardDutyの検出タイプ: TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver ・対象EC2インスタンス: arn:aws:ec2:ap-northeast-1:111122223333:instance/i-0123456789 ・リモートIPアドレス: 1.2.3.4/32 または 1.2.3.5/32 これを Cedar ポリシーにしてみます: permit ( principal, action == GuardDutyApp::Action::"check", resource ) when { context has accountId && context.accountId == "111122223333" && context has findingType && context.findingType == "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" && context has ec2InstanceArn && context.ec2InstanceArn == "arn:aws:ec2:ap-northeast-1:111122223333:instance/i-0123456789" && context has remoteIpAddress && ["1.2.3.4", "1.2.3.5"].contains(context.remoteIpAddress) }; 判定に必要な属性のみを条件としています。属性はすべて「必須」とはしていないため、リク エス トに含まれていない可能性があります。そのため has オペレータでまずは存在を確認しています。 また、 OR 条件である「リモート IPアドレス 」は、 .contains() オペレータを利用しています。 ※ has で属性の存在確認をしないと、必須ではない属性に関しては Cedar の VS Code 拡張 利用時にエラーが表示されます。 ポリシーの例2 続いて案件 B の判定条件: <通知スキップの条件> ・アカウントID: 444455556666 ・GuardDutyの検出タイプ: TTPs/Exfiltration:S3-AnomalousBehavior ・操作対象のS3バケット名: 「my-log-bucket-」から始まるバケット名 ・発生時間帯: 毎月1日の 2:00AM ~ 6:00AM これを Cedar ポリシーにしてみます: permit ( principal, action == GuardDutyApp::Action::"check", resource ) when { context has accountId && context.accountId == "444455556666" && context has findingType && context.findingType == "TTPs/Exfiltration:S3-AnomalousBehavior" && context has s3BucketName && context.s3BucketName like "my-log-bucket-*" && context has date && context.date == 1 && context has hour && context.hour >= 2 && context.hour <= 6 }; バケット 名の部分一致については、 like オペレータで ワイルドカード を利用できます。 「毎月1日の 2:00AM ~ 6:00AM」という範囲による判定条件は不等号 ( >= 、 <= )が利用できます。 評価結果の例 以上の2つのポリシーを作成したとき、実際に Verified Permissions にリク エス トを送信した判定結果をいくつか示します。 案件 A のポリシーに該当する条件、リモート IP アドレスが 1.2.3.4 : コンテキスト属性 値 accountId "111122223333" findingType "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" ec2InstanceArn "arn: aws :ec2:ap-northeast-1:111122223333:instance/i-0123456789" remoteIpAddress "1.2.3.4" 結果: ALLOW 案件 A のポリシーに該当する条件、リモート IP アドレスが 1.2.3.5 : コンテキスト属性 値 accountId "111122223333" findingType "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" ec2InstanceArn "arn: aws :ec2:ap-northeast-1:111122223333:instance/i-0123456789" remoteIpAddress "1.2.3.5" 結果: ALLOW 案件 A のポリシーに該当する条件、判定を左右しない追加属性がある: コンテキスト属性 値 accountId "111122223333" findingType "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" ec2InstanceArn "arn: aws :ec2:ap-northeast-1:111122223333:instance/i-0123456789" remoteIpAddress "1.2.3.4" date 10 hour 10 結果: ALLOW 案件 A のポリシーに近いが、リモート IP アドレスが通知スキップ条件に該当しない: コンテキスト属性 値 accountId "111122223333" findingType "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" ec2InstanceArn "arn: aws :ec2:ap-northeast-1:111122223333:instance/i-0123456789" remoteIpAddress "1.2.3.6" 結果: DENY 案件 A のポリシーに近いが、リモート IP アドレス属性が不足: コンテキスト属性 値 accountId "111122223333" findingType "TTPs/Defense Evasion/DefenseEvasion:EC2-UnusualDNSResolver" ec2InstanceArn "arn: aws :ec2:ap-northeast-1:111122223333:instance/i-0123456789" 結果: DENY 案件 B のポリシーに該当する条件、S3 バケット 名が my-log-bucket-1 : コンテキスト属性 値 accountId "444455556666" findingType "TTPs/Exfiltration:S3-AnomalousBehavior" s3BucketName "my-log- bucket -1" date 1 hour 2 結果: ALLOW 案件 B のポリシーに該当する条件、S3 バケット 名が my-log-bucket-abcdefg : コンテキスト属性 値 accountId "444455556666" findingType "TTPs/Exfiltration:S3-AnomalousBehavior" s3BucketName "my-log- bucket -abcdefg" date 1 hour 2 結果: ALLOW 案件 B のポリシーに近いが、S3 バケット 名が通知スキップ条件に該当しない: コンテキスト属性 値 accountId "444455556666" findingType "TTPs/Exfiltration:S3-AnomalousBehavior" s3BucketName "other- bucket " date 1 hour 2 結果: DENY 案件 B のポリシーに近いが、発生時間が通知スキップ条件に該当しない: コンテキスト属性 値 accountId "444455556666" findingType "TTPs/Exfiltration:S3-AnomalousBehavior" s3BucketName "my-log- bucket -1" date 1 hour 7 結果: DENY 案件 B のポリシーに近いが、発生時間が数値型ではなく文字列型: コンテキスト属性 値 accountId "444455556666" findingType "TTPs/Exfiltration:S3-AnomalousBehavior" s3BucketName "my-log- bucket -1" date 1 hour "2" 結果: DENY SDK で Verified Permission を呼びだす例 AWS SDK for JavaScript を利用して Verified Permission を呼びだすコード例を書いておきます。 (受け取った GuardDuty のアラートから該当するフィールドのデータを取り出す部分は省略しています。) import { VerifiedPermissionsClient , IsAuthorizedCommand } from "@aws-sdk/client-verifiedpermissions" ; const client = new VerifiedPermissionsClient ( { region: "ap-northeast-1" } ); // GuardDuty アラートから内容を取りだす(省略) const accountId: string = // アカウントID const findingType: string = // GuardDuty 検出タイプ const ec2InstanceArn: string = // 対象 EC2 インスタンス ARN const s3BucketName: string = // 対象 S3 バケット名 const remoteIpAddress: string = // リモートIPアドレス const date: number = // 検出日時(日付) const hour: number = // 検出日時(時間) const minute: number = // 検出日時(分) const command = new IsAuthorizedCommand ( { // マネジメントコンソールから確認できる Verified Permissions のポリシーストア ID policyStoreId: "DUMMYSTOREID" , // アクションは常に check action: { actionType: "GuardDutyApp::Action" , actionId: "check" } , // コンテキストに属性を追加 context: { contextMap: { accountId: { string : accountId } , findingType: { string : findingType } , ec2InstanceArn: { string : ec2InstanceArn } , s3BucketName: { string : s3BucketName } , remoteIpAddress: { string : remoteIpAddress } , date: { long : date } , hour: { long : hour } , minute: { long : minute } , } , } , } ); client .send ( command ) .then (( result ) => { console .log ( result.decision ); // 判定結果 console .log ( JSON .stringify ( result.determiningPolicies )); // 該当したポリシー if ( result.decision === "ALLOW" ) { // 通知スキップ時の処理 } else { // 通知時の処理 } } ) . catch (( e: unknown ) => { // コマンドエラー時の処理 } ); Verified Permissions のサービスクオータ Verified Permissions の利用に際してはサービスクオータを念頭に置いておくと良いです。 ドキュメント には次のように記載されています。 単一リソースに関するすべてのポリシーの合計サイズは、200,000 バイトを超えることはできません。 1つのポリシーストアに作成できるポリシーの数にはクオータは設定されていないようですが、ポリシーサイズにはクオータがあります。ポリシーがリソース( resource )を指定している場合、同じリソースを指定している全てのポリシーの合計サイズは 200,000 バイトが上限とのことです。今回の使い方では resource を指定していませんが、サポートに問い合わせたところ resource を指定していない全てのポリシーの合計サイズの上限が 200,000 バイトになる そうです。またポリシー内に記載しているコメントや、改行文字、空白文字もサイズとしてカウントされるそうです。ポリシー数が多くなることが予想される場合には留意しておきましょう。 ポリシーのサイズを節約したい場合は、 以前の記事 の通り CDK でポリシーの管理やデプロイを行い、デプロイ時に CDK のコードでポリシーファイル内のコメント行を削除するなどの処理を入れても良いかもしれません。 また、 Cedar のベストプラクティス によると、可能であれば ポリシーで resource を指定するとパフォーマンスメリットがあるとのことです。今回はどのような条件でスキップが判定されるのかわからない想定のため resource を指定せずに利用しましたが、例えば常にアカウント ID が判定条件に使われることが分かっているのであれば、 context ではなく resource にアカウント ID を指定しても良いかもしれません。判定時にパフォーマンスの向上が期待でき、 resource も分散するのでサービスクオータに引っかかりにくくなります。 まとめ Verified Permissions を汎用ポリシー判定エンジンとして利用し、GuardDuty アラートを社内案件に通知するかどうかの条件判定に使ってみました。Cedar 言語の表現力のおかげで、複雑な判定ロジックを自前で実装せずに済み、ポリシーを作成するだけで利用できるのが嬉しいところです。工夫をすれば認可以外でも Verified Permissions を便利に利用できるケースはあると感じました。 執筆: @kou.kinyo 、レビュー: @kano.nanami ( Shodo で執筆されました )
アバター