ℹ️

衛星運用のAgent化第一歩:生成AIを呼び出すSlack Botのセキュアな基盤構築

に公開

衛星運用のような専門的な社内業務において、GPTやGemini等の生成AI を活用して業務効率化を図ることは喫緊の課題です。今回は、そのための最初のインターフェースとして、社内メンバーの自然言語による問い合わせ(例えば「次の衛星画像のダウンリンク時間」)に対し、生成AIを通じて解析・応答するSlack Bot AgentをGCP上に構築する際の、セキュアで運用工数の少ない基盤アーキテクチャを確立します。

これは、将来的に衛星運用を高度なAgent機能で自律化していくための第一歩となります。

やりたいこと

  • 特定のSlackチャンネルでのみ動作を許可された「チャンネル限定型」Bot Agentを構築する。
  • Slackをインターフェースとし、生成AI (GPT, Geminiなど) を呼び出して自然言語クエリを解析・処理し、社内APIを通じて応答する。
  • ボットの運用工数を最小限に抑え、もともとGCPで構築されている自社システムとの連携も含め、GCP上で完結させる。
  • 一般的なセキュリティ要件、特に機密情報の管理とアクセス制限に対応する。

使える資源、制約

  • ホスティング環境として、GCPのサーバーレス実行環境を採用し、GCP上で完結させる。
  • インフラストラクチャの定義には、再現性を保つためにIaCとしてTerraformを使用する。
  • Slackからのリクエストは、**署名シークレット(Signing Secret)**による検証を必須とする。
  • Slackの応答要件(3,000ミリ秒以内)に対応できるアーキテクチャが必要となる。

1. 生成AIを活用した業務効率化に向けたアーキテクチャ

このBotの核となるのは、ユーザーの自然言語クエリを、社内APIが実行できる構造化されたコマンドに変換するAgent機能です。この自然言語処理には、GeminiやGPT-4o といった生成AIの呼び出しや、複雑なビジネスロジックの実行が伴い、処理に時間を要する可能性があります。

Slackの要求する厳格な応答時間要件 を満たしつつ、長時間かかるAgent処理を実行するために、即時応答/遅延処理パターン を採用し、処理を同期層と非同期層に分離します。

推奨されるGCPアーキテクチャ(Cloud Run + Pub/Sub)

  1. Cloud Run A (Synchronous Handler / APIゲートウェイ):
    • 責務: Slackからのリクエストを受信したら、まず署名検証チャンネル限定検証 を迅速に実行します。
    • 動作: 検証が成功したら、リクエストペイロード全体をPub/Subにエンキューし、即座に SlackへHTTP 200 OKを返却します。
    • 必要なシークレット: SLACK_SIGNING_SECRET
  2. Cloud Run B (Asynchronous Worker / Agent Worker):
    • 責務: Pub/Subからのプッシュ通知によってのみ起動されます。
    • 動作: 生成AIを呼び出し、自然言語クエリを解析・処理し、その結果を用いて自社APIを呼び出し、衛星データ(ダウンリンク時間など)を取得します。
    • 応答: 処理完了後、ペイロード内のresponse_urlを使用して、非同期でユーザーに最終結果を通知します。メッセージ投稿のためにSLACK_BOT_TOKENが必要です。

2. セキュリティとアクセス制御:「チャンネル限定型」の厳格な担保

社内機密情報にアクセスするBotであるため、「チャンネル限定型」という要件を二重の防御で実現します。

2.1. Botスコープの厳選と最小権限

Botはアプリ自身として動作し、特定のユーザーに紐づかない Botトークン(xoxb-) の使用が強く推奨されます。

チャンネル限定型Botが、イベント受信、検証、投稿を行うために必要な最小限のBotスコープは以下の通りです:

スコープ 目的と必要性
commands Slash Commandの有効化に必須。
chat:write Bot名義で応答メッセージを投稿するために必須。
channels:read パブリックチャンネル情報の読み取り(チャンネル検証用)に必要。
groups:read プライベートチャンネル情報の読み取り(チャンネル検証用)に必要。

2.2. アプリケーションロジックによるチャンネル検証

OAuthスコープ設定に加え、アプリケーションコード(Cloud Run A)内で、受信したイベントが 許可されたチャンネルIDリスト(ホワイトリスト) 内から来たものであるかを確認するロジックを実装します。

  1. channel_idの抽出: 受信したペイロードからchannel_idを取得します。
  2. ホワイトリスト照合: 内部で安全に管理している許可リストと照合します。
  3. 拒否戦略: 照合に失敗した場合、Botは即座に処理を中断 し、社内APIへの連携を防ぎます。セキュリティを優先し、エラーメッセージを応答せずに 「沈黙する」 こと が推奨されます。

2.3. Slackリクエストの真正性検証

Cloud Run Aは、リクエストを受け取った際に、Slackから送られたリクエストが偽装されていないかを検証するため、以下の署名検証を迅速に実行します。

  • Secret ManagerからSLACK_SIGNING_SECRETを取得し、X-Slack-Request-Timestampヘッダーの検証 を通じて、リプレイ攻撃を防止します。

  • nodeJS であれば @slack/bolt を利用して署名検証を実装します

3. 最小運用とセキュアなGCP基盤

運用工数を最小限に抑え、セキュアにBotを運用するためには、GCPのサービスを適切に連携させる必要があります。

3.1. シークレット管理と最小権限(Secret Manager)

Botトークンや署名シークレットは、GCPの Secret Manager で一元管理します。

  • IAM設定: Cloud Runサービス(A/B)の実行サービスアカウント(SA)には、シークレットの内容を読み取るための最小限の権限である roles/secretmanager.secretAccessor ロールのみを付与します。これにより、SAは管理や編集の権限を持ちません。
  • シークレット注入: Cloud Run V2サービス定義で、シークレットを環境変数として安全にマウントします。

TerraformによるIAM設定の具体例

# Cloud Runサービスアカウントがシークレットにアクセスする権限を付与
resource "google_secret_manager_secret_iam_member" "slack_signing_secret_accessor" {
  secret_id = google_secret_manager_secret.slack_signing_secret.secret_id
  role      = "roles/secretmanager.secretAccessor" 
  member    = "serviceAccount:${google_service_account.cloud_run_sa.email}" 
}

# Secretを環境変数としてマウントする設定例
resource "google_cloud_run_v2_service" "handler_a" {
  #... 省略
  template {
    containers {
      image = "us-docker.pkg.dev/..."
      env {
        name = "SLACK_SIGNING_SECRET"
        value_source {
          secret_key_ref {
            secret = google_secret_manager_secret.slack_signing_secret.secret_id
            version = "latest"
          }
        }
      }
    }
  }
}

3.2. サービス間認証チェーンの確立(Pub/Sub Push)

Cloud Run B(非同期ワーカー)は、外部に公開されず、Pub/Subからのプッシュによってのみ起動されるべきです。このサービス間通信には、OIDC (OpenID Connect) JWTトークン を使用したセキュアな認証が必要です。

この認証を機能させるためには、以下のIAM設定が 必須 となります。

  • Pub/Subサービスエージェントservice-{PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com)に対し、ターゲットのCloud Run Bサービスへの**roles/run.invoker**権限を付与します。

TerraformによるPub/Sub Invoker権限設定の具体例

# Pub/SubサービスエージェントにCloud Run BへのInvoker権限を付与
resource "google_cloud_run_iam_member" "pubsub_invoker_role" {
  location = google_cloud_run_v2_service.worker_b.location
  service  = google_cloud_run_v2_service.worker_b.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:${local.pubsub_service_account}"
}

4. まとめ、今後の展開

今回のアーキテクチャは、Slackを生成AIを活用した社内業務効率化のインターフェースとして活用するための、セキュアでスケーラブルな基盤となります。このBot Agentが、自然言語での問い合わせに対して自律的に社内システムを操作できるようになれば、衛星運用におけるAgent化の大きな一歩となります。(Agentが生成AIをどう利用するかのは別の記事にまとめます)

SlackAgentアーキテクチャの要点:

  1. Cloud Run + Pub/Subによる応答分離で、Agent処理の実行をバックグラウンドにオフロードし、ユーザー体験を維持。
  2. BotトークンとチャンネルIDホワイトリスト照合で、厳格な「チャンネル限定型」アクセスを二重に担保。
  3. Secret Managerと**secretAccessorロール**の適用により、機密情報を最小権限で管理。
  4. Pub/Subサービスエージェントへのrun.invoker権限付与により、非同期ワーカーへのセキュアなPush認証を確立。

痛感したこと

SlackBotの構築自体は昔から事例が沢山あるのでそんなに大変でないと思っていました。ただ、細かいところやクラウド基盤に応じて実際に考慮するところはたくさんあるなと気付かされます。
生成AIを用いたAgent開発を進める中で、LLM自体の性能向上はもちろん重要ですが、、サービス間連携における認証チェーン や認可の取り扱いに人間側が正確な知識を持って臨むことがより重要度がたかまっているとおもいます。生成AIがコードはかいてくれるようになりましたが、こういった要件、設計前提知識を理解して 指示を正しくだすところに人間の役割はまだありそうです。

シンギュラリティ・ソサエティ

Discussion