Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

ChatGPTで始めるAWS Security Hubの運用最適化

最近はもうLLMがないと開発できなくなったcw-ozakiです。

みなさんはChatGPTを業務に組み込んでいますか? Chatworkでの活用例として4月ぐらいからChatGPTを使ってAWS Security Hubの運用を改善しようとしていたので、そちらの取り組みついて紹介します。

Security Hub Assistant

次世代開発基盤チームでは月に1度、運用レポートを書いており、Security Hubのスコアもレポートの対象としてトラッキングしています。 総じて高いスコアを保つことはできていますが、最低限やることをやって何とかスコアを保っているのが実情で、運用のサイクルに上手くハマっていないと感じでいます。

では、どこにSecurity Hubを運用する難しさがあるのかというと

  1. リソースとルールが多く検知が多い
  2. 検知された内容にノイズが多い
  3. Security Hubのライフサイクルが難しく、手動運用では想定外のステータス遷移に遭遇しやすい

といったあたりで、検知内容のトリアージ、対応の手間の大きさから根付かないといったあたりに難しさがあるなと感じています。

もちろん、例えば1の内容であれば、そもそも検知されるリソースを作れないようにガードレールを設営する、対応が不要ならルール自体を無効化することも大事です。 しかし、それでもまだ完全に検知をなくせるわけではなく、特に条件付きで検知されるリソースを許容するケースなど一定数残ってしまいます。 (例えばAutoScaling.2でAZはMulti-AZにすべきですが、弊社のEKSの運用ではAZ単位にASGを作成しているため対応が不要になるなど)

そこで、Security Hub Assistantという形でChatGPTを活用して2、3の解決を行いました。

設計ポリシー

Security Hub Assistantを作るにあたり、いくつか考慮しないといけない事情がありました。

  1. 私自身、他にメインの業務を持っており、あまり時間をかけることができない
  2. Chatworkでの社内利用としてChatGPTの利用は初めてであり、先行事例として確実に成功させる必要がある

こういった事情がなければAgent方式で必要なリソース情報を自力で集めて判断するAIの構築なども考えれました。が、最短で確実に作り上げるためにルールベースで判断してトリアージするAIを構築していきます。 このルールベースというのが味噌で、個人的にはプログラムで記述可能だがパターン数が多すぎて保守が現実的ではないものはChatGPTの得意領域の1つだと思っています。最悪、プログラムで記述可能なのでプログラムを書いて逃げることもできますし。

システム構成

Security Hub Assistantの構成は下記のようになります。

ここでdeveloper account側に2つあるLambdaでOpenAI APIを呼び出します。

Security HubのトリアージをするLambda

トリアージするさいに使うプロンプトはかなりシンプルなものになります。

Finding:の内容にRule:はマッチしますか?
マッチするならsuppress、マッチしないならnotifyを返してください。

Rule:
{{rule}}

Finding:
{{finding}}

{{rule}}には自然言語で記述したルールを、{{finding}}にはSecurity Hub FindingsのJSONを入れ込んでます。 ここでレスポンスとしてはsuppress、またはnotifyと回答がくるので、それに合わせてSecurity HubのAPIを実行して抑制するか、Chatworkに通知を行います。

また、Findingsの情報だけでは足りないことが多いのでResource Explorerからリソース情報を含めるケースもあります。 ただし、こちらはResource Explorerでリソースを発見できないこともあるため、あれば含めるようにしています。

ChatOpsでルールとFindingsを管理するLambda

こちらはOpenAI APIのFunction Callingの機能を使って作成しています。

[
  {
    name: 'updateFindingStatusToSuppressed',
    description: 'FindingのステータスをSUPPRESSED(抑制)に更新',
    parameters: {
      type: 'object',
      properties: {
        findingId: {
          type: 'string',
          description: 'Security Hubのイベントに指定されたFindingのID (Findingの.findings[0].Idの値)',
        },
        productArn: {
          type: 'string',
          description:
            'Security Hubが生成するARNで、調査結果を生成する製品を一意に識別します。これは、Security Hubと統合されているサードパーティ製品のARN、またはカスタム統合のARNになります(Findingの.findings[0].ProductArnの値)',
        },
        reason: {
          type: 'string',
          description: 'ユーザーによって説明された抑制の理由。理由がない場合は省略する',
        },
      },
      required: ['findingId', 'productArn'],
    },
  },
  {
    name: 'addAutomaticSuppressionRule',
    description: '自動抑制ルールを追加する',
    parameters: {
      type: 'object',
      properties: {
        controlId: {
          type: 'string',
          description: 'ルールを適用するコントロールのID([IAM.6、EC2.3といった形式のFindingのControlID)',
        },
        condition: {
          type: 'string',
          description: '自動で抑制する条件。常に ~の場合 といった形式で指定する',
        },
        reason: {
          type: 'string',
          description: 'ユーザーによって説明された抑制する理由。理由がない場合は省略する',
        },
      },
      required: ['controlId', 'condition'],
    },
  },
  {
    name: 'deleteAutomaticSuppressionRule',
    description: '自動抑制ルールを削除する',
    parameters: {
      type: 'object',
      properties: {
        controlId: {
          type: 'string',
          description: 'ルールを適用するコントロールのID([IAM.6、EC2.3といった形式のFindingのControlID)',
        },
      },
      required: ['controlId'],
    },
  },
  {
    name: 'listAutomaticSuppressionRules',
    description: '自動抑制ルールを取得する',
    parameters: {
      type: 'object',
      properties: {},
    },
  },
]

ユーザーが入力したメッセージと過去のやりとりから、Function Callingで対応する関数を選択して対応する処理を実行する形です。

ここが運用の中核になるのでFunction Callingで何をできるようにするかが重要になります。

例えばupdateFindingStatusToSuppressedは適切にルールを設定し、トリアージができていればなくても良いFunctionになります。 しかし、ChatGPTでのトリアージでは必ずトリアージに成功する訳ではないということを踏まえると、ChatOpsで気軽に抑制できることは大切になります。 また、人間がルールを事前に全て設定できる訳ではありません。 そこで、トリアージで漏れてきたSecurity Hubの検知内容をあとからフォローできる運用フローを構築することが大切です。

実際に動かしているところ

このケースは通知されたファインディングを直接抑制するケースですが、上手く動いてそうですね! ちなみに詳しくは解説していませんが、通知内容の翻訳や、リソースエクスプローラーからの情報の追加、メモリーとして会話の保持などもやっています。

そのため、間違っている使い方ですが、こういったこともできます。 RAGなどを活用すれば通知内容の詳細の解説や、対応方法の提示などもできるとは思いますが、この辺りは今後の改善内容です。

作ってるときに起きた問題

この仕組みは2023/4から毎日1時間、ちまちまと作っており、だいたい5月上旬ぐらいにはできあがり動かして遊んでいました。 が、6月に大事件がおきます。

https://aws.amazon.com/jp/about-aws/whats-new/2023/06/aws-security-hub-automation-rules/

そう、Security Hub Automation Ruleの発表です。 なんで・・・なんで作る前に出してくれなかったの・・・!!!ということで、やる気を失ったのと、Security Hub Automation Ruleの運用を詰めるのでだいたい2ヶ月ほど飛びました。

https://openai.com/blog/function-calling-and-other-api-updates

また、6月にOpenAIからFunction Callingのサポートと新しいモデルの公開がされました。

入力されたChatworkのメンションイベントから、ユーザーが期待するアクションをアクションリスト:に列挙されたアクションから選択してください。

アクションリスト:
- registerRule(controlId: string, condition: string, replyActionCompletedMessage: string): void | SecurityHubのコントロールIDに対して抑制条件を設定に登録する。conditionは「[条件] の場合は抑制する」というように入力から条件を抽出して簡潔に条件をわかるようにしなければならない
- unregisterRule(controlId: string, replyActionCompletedMessage: string): void | SecurityHubのコントロールIDに対して登録されている抑制条件を設定から削除する
- replyMessage(message: string): void | 現在の設定:、コンテキスト:に関連する問い合わせ、他の一致するアクションがない場合はこのアクションを選択します。入力されたChatworkのメンションイベントに対して返信するメッセージを引数に渡してください

回答はJSON形式で回答してください:
{
  "action": "選択したアクション名",
  "args": [
    "選択したアクションに引き渡す引数"
  ]
}

もともとこういうようなプロンプトでFunction Calling機能を実現していたので置き換え確定です。 ちなみにですが、Function Callingと自作プロンプトだと自作プロンプトの方が精度がいいです。代わりにJSON出力で失敗することがあるため、そのあたりの補助の実装が必要なこと、Function Callingの方が意図を汲みやすく複数人で開発するならそちらの方が意図の共有がしやすいです。

余談ですが、Function CallingでSecurity Hub Automation Ruleのルールを生成するのは上手くいきませんでした。 これはAutomation Ruleのリクエストのパラメーターが多く、人間でもこれの呼び出しは難しい・・・という状態のため、あまり現状のChatGPTでは適切にルールを作るのは難しそうです。 調整すればやってやれないこともない印象ではありますが、パラメーターが本当に多いため調整も一苦労です。

作ってみた知見

ChatGPTは銀の弾丸ではない

ChatGPTによってこれまで行うことが難しかったことが簡単にできるようになりました。 が、ChatGPTがあれば全てを解決できるかというとそんなことはありません。

特にこういった運用改善系では運用フロー自体を見直し、自動化しやすい形にしていくことが最も重要です。

では、どういった箇所でChatGPTを活用するかというと、これまで運用フロー上で人間による判断が必要になっていた箇所は置き換えることで効果を大きくできる印象です。 特に自動化できるがパターンが膨大で現実的に運用・保守が厳しいといった箇所に刺さりそうだと考えています。

モデル・プロンプトの品質をどう検証するか

エンジニアなので何かモデルやプロンプトを弄ったら問題ない修正かはCIで検証したいです。 現状ではユニットテストでOpenAI APIをコールして、プロンプトが期待した結果になるか正解率を見ています。

しかし、この方法にはいくつか問題があります

  • APIコール数が増えるので費用がかかる
  • APIコール数が増えるのでテストの実行に時間がかかる
  • 確率で失敗するためフラジャイルなテストになる
  • どのくらい正解すればいいのか閾値が設定しにくい

また、この方法でテストを通したとしても、実際にドッグフーディングすると物足りないケースはやはり出てきます。 そう考えると費用対効果が低すぎるので何か別の方法を考えたいです。

おわりに

ChatGPTでこれまでやるのが手間だった運用改善がいろいろできるようになりました。 次は実際に運用状況を見ながら何かあれば対応を・・・とか言ってたらOpenAI Dev Day 2023でまた新しくいろいろ出ましたね! Assistant APIが来たため、メモリー周りや、AWSのドキュメントをRAGしたいなど考えるとこれは作り直しでは・・・と戦々恐々としています。

片手間でやってると作ってるものもすぐに陳腐化するし、アウトプット書くのもぐずぐずしてるとこういうことになるので、すぐ作って、すぐアウトプットするの大事ですよ!!!