コンテンツにスキップ

クレデンシャル配布 (テーマリポ Claude Code セッション)

コントロールプレーンと責務分担 で確定した「テーマリポ Claude Code セッション = コントロールプレーン」を成立させるための、メディア運営者へのクレデンシャル配布設計。Issue #106 確定事項。

TL;DR

  • 配布ハードは AWS SSO ログイン一本。メディア運営者は aws sso login --profile spindd-<tenant> を 1 日 1 回実行するだけ
  • AWS IAM Identity Center (ssoins-18083f7232cea59f / us-east-1) を使う
  • MFA 必須
  • Secrets Manager の Read は skill が都度取得 → メモリ内のみで使用 (運営者は生キーを直接扱わない)
  • Write は二段構成:
    • 初回 bootstrap (placeholder secret + IAM policy 整備) はシステム管理者 (automedia 開発者)
    • 以後の routine 更新・チャネル追加時の値投入はメディア運営者がセルフサービス で実施 (Issue #106 確定)
    • チャネル発行作業 (LINE OA / HubSpot Private App / Backlog API キー再発行) は当然運営者が当該管理画面で行う
  • secret-watch Lambda (Issue #79) が期限を週次監視

全体フロー

クレデンシャル配布フロー

関係者と責務境界

関係者 担当 触らない
メディア運営者 (spin-dd 運営担当) aws sso login (1 日 1 回) / Claude Code セッション操作 / Backlog UI で起票・承認 / チャネル発行 (LINE OA / HubSpot Private App / Backlog API キー再発行) / 取得した token を skill 経由で Secrets Manager に投入 (routine 更新・チャネル追加) IAM 設定 / 初回 placeholder secret 作成 / OpenTofu
automedia 開発者 (= システム管理者) 初回 bootstrap (placeholder secret 作成 + IAM Permission Set 整備) / Identity Center ユーザー管理 / OpenTofu 全般 / secret-watch Lambda 運用 / 不正アクセス時の無効化 テナント固有のチャネル発行操作 (= 運営者の領分)
AWS IAM Identity Center SSO 認証 + MFA / Permission Set → IAM Role の解決 / 短期 STS クレデンシャル発行 (マネージド)
secret-watch Lambda 週次で Secrets の expires_at tag と最終更新からの経過日数を監視 → CloudWatch Alarm (自動)

routine 更新の責務移譲モデル

チャネルオンボーディング のシナリオでは「メディア運営者が LINE OA を新規作成 → channel access token を取得 → automedia に投入」までを 管理者の介在なしで完結 させる必要がある。そのため:

  • 管理者は 初回テナント立ち上げ時に placeholder secret を作成 + 該当テナントの Permission Set に secretsmanager:PutSecretValue/automedia/<tenant>/* 配下限定で付与
  • 以後、運営者は Claude Code skill (automedia-secret-set) 経由でセルフサービス更新
  • skill は構造バリデーション (JSON スキーマ) + 監査ログ (誰がいつ何を更新したか CloudTrail に残る) を担保

クレデンシャル一覧

用途 アクセス先 認証手段 配布方法 期限・ローテーション
read API Lambda 呼び出し (preview / status / audience-query) AWS API Gateway SigV4 (assumed IAM Role) SSO 経由で自動 短期 STS (max 12h)、SSO 再ログインで更新
Secrets Manager /automedia/<tenant>/<provider> 閲覧 AWS SDK 同上 同上 同上
Backlog API キー spindd.backlog.com API キー (Bearer) Secrets Manager → skill が GetSecretValue で都度取得 個人キー、推奨 90 日。運営者が Backlog 個人設定で再発行 → /automedia-secret-set で投入
HubSpot Service Key (or Private App Token) HubSpot API Bearer 同上 発行時 expires_at を tag で記録、secret-watch が週次監視。運営者が Portal で再発行 → /automedia-secret-set で投入
LINE Channel Secret / Access Token LINE Messaging API Header 同上 LINE Developer Console で再発行 → /automedia-secret-set で投入 (新規 OA 作成も同経路)
LINE OA Manager (リッチメニュー等の手動運用) LINE Business アプリ / Web ID/PW (運営者がブラウザでログイン) 個別管理

運営者経路の仕様

運営者の具体的な手順 (メディア運営者ハンドブック) と チャネル別オンボーディング手順 (14b) を参照。本節は内部仕様 (SSO config の構造 / 切替時の安全性 / /automedia-secret-set skill の挙動) のみを扱う。

SSO config の構造

運営者 PC の ~/.aws/config には per-tenant profile が並び、すべて単一の [sso-session] を共有する設計:

[profile spindd-bato]
sso_session = spindd
sso_account_id = 695590128753
sso_role_name = TenantOperator-bato
region = ap-northeast-1
output = json

[profile spindd-okada]
sso_session = spindd
sso_role_name = TenantOperator-okada
# ... 担当テナント分繰り返し

[sso-session spindd]
sso_start_url = https://<spindd-domain>.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access
  • sso_session 共有: aws sso login --sso-session spindd 1 回で全 profile が有効化 (MFA 1 回 / 8h)
  • profile 切替の物理的安全性: 各 Role の inline policy で Resource ARN/automedia/<tenant>/* 限定にしているため、profile を取り違えても他テナントの secret を IAM が拒否する
  • CloudTrail 監査: Role 別 (TenantOperator-bato / TenantOperator-okada) に記録されるため、後追いで誤操作を特定可能
  • /automedia-init による事前検証: skill がテーマリポ project.json > aws.profile と現在の AWS_PROFILE の一致を検証 → 誤テナント操作時は即時 abort

config snippet は 14a §11 Phase 1operator_welcome_<tenant> output で生成され、管理者から運営者へ伝達される。

/automedia-secret-set skill の内部挙動

運営者が /automedia-secret-set provider=<...> を実行した時の処理:

  1. --tenant 引数 (将来 project.json > tenant) と AWS_PROFILE から secret ARN を解決 (/automedia/<tenant>/<provider>)
  2. 既存値を GetSecretValue で取得 (差分確認用、生キーは画面表示しない)
  3. 入力プロンプトで新値を受け取る (process.stdin、echo off で伏字)
  4. JSON スキーマで構造検証 (例: LINE なら channel_id / channel_secret / access_token 必須)
  5. PutSecretValue で更新
  6. 更新者 (CallerIdentity) と日時を tag: last_updated_by, last_updated_at で記録
  7. CloudTrail / Secrets Manager のアクセスログにも自動記録

これにより 生キーがローカルファイル / ターミナル履歴に残らない + 監査が一貫する。

project.json への aws.profile フィールド (#113)

設定ファイルの実体は .automedia/project.json (sync Lambda が S3 へミラー、deliver が読む)。 ここに運営者ローカルの AWS profile を明示する:

{
  "channels": { "line": { "enabled": true }, "hubspot": { "enabled": true } },
  "ai": { "enabled": true, "model": "claude-sonnet-4-6" },
  "aws": { "profile": "spindd-bato" }
}
  • automedia-init skill は project.json > aws.profile を読み、AWS_PROFILE 未設定なら export AWS_PROFILE=<profile> を案内 + その profile で検証する (サブプロセスは親シェルの env を変更できないため「自動設定」ではなく「検証 + 案内」)。
  • aws.profileclient 側関心で deliver は runtime で使わない。sync Lambda は project.json 同期時に aws.profile / channels の欠落を検証し、欠落時は CloudWatch メトリクス automedia/Sync > ProjectConfigInvalid → Alarm で通知する (S3 put 自体は ブロックしない)。

システム管理者の責務

初回オンボーディング (運営者 1 名追加)

詳細手順は 14a §9 運営者への引き継ぎ案内 を参照。要点 (運営者ごとに毎回実施):

  1. /automedia-operator-onboard --send-invite --apply で運営者ユーザー作成 + Permission Set (TenantOperator-<tenant>) 割当 + SES 招待メール送信を 1 コマンドで完遂 (#115 / #174 / #188)
  2. 運営者は届いた招待メールに従って初期パスワード設定 + MFA 登録 (Authenticator アプリ等)
  3. SSO profile snippet は招待メール本文に同梱されているため、運営者は /automedia-aws-config-setup~/.aws/config に取り込むだけ

前提 (AWS アカウント単位 / 1 回のみ): IdC Console > Settings > Authentication で 2 箇所設定済であること (14a 前提要件): 1. Standard authentication で Send email OTP を有効化 (パスワード設定経路の開通) 2. Multi-factor authentication で 「サインイン時に MFA デバイスを登録するよう要求する」 を選択 (MFA 強制登録)

両設定とも公開 API/CLI では変更も状態確認も不可 (Console 専用)。運営者追加のたびに再設定する必要はない。

フォールバック (verification メール不達時): 管理者が IdC Console > Users > Reset password > Generate a one-time password で OTP を発行し、運営者へ別経路で渡す (14a §9-2)。公開 API は無い (Console 専用、AWS re:Post)。

新規テナント立ち上げ時の bootstrap (= 運営者がセルフサービスできるための前提作業)

  1. Secrets Manager に placeholder secret を作成 (automedia-secret-watchexpires_at tag も初期値を仮設定):
    aws secretsmanager create-secret \
      --name /automedia/<tenant>/line \
      --secret-string '{"channel_id":"_placeholder","channel_secret":"_placeholder","access_token":"_placeholder"}' \
      --region ap-northeast-1 --profile spindd-admin
    aws secretsmanager create-secret --name /automedia/<tenant>/hubspot --secret-string '{"access_token":"_placeholder"}' ...
    aws secretsmanager create-secret --name /automedia/<tenant>/backlog --secret-string '{"api_key":"_placeholder","webhook_secret":"...","space_key":"spindd"}' ...
    
    → コードは placeholder 値 (_placeholder prefix) を検出して該当 provider の処理を no-op にする (既存パターン、webhook-line Lambda の HubSpot 同期 skip と同じ)
  2. Permission Set に PutSecretValue を /automedia/<tenant>/* 限定で付与 (詳細は下の OpenTofu 例)
  3. 運営者へ「以後の credential 投入は /automedia-secret-set skill を使ってください」と案内

管理者にしかできない対応

操作 理由
新規テナント secret の CreateSecret 初回 bootstrap、IAM ポリシーと同時整備が必要
Permission Set / IAM Role の変更 OpenTofu 経由、運営者は実行権限なし
secret-watch Lambda 運用 テナント横断、システム監視
不審アクセス検知時の即時無効化 Identity Center 操作
完全削除 (DeleteSecret) 業務影響大、運営者には付与しない

緊急時の手動更新 (運営者が対応できないケースのみ)

# 例: 運営者が休暇中に LINE トークンが失効した等、緊急時のみ
$ aws secretsmanager put-secret-value \
    --secret-id /automedia/<tenant>/line \
    --secret-string '{"channel_id":"...","channel_secret":"...","access_token":"<NEW>"}' \
    --region ap-northeast-1 --profile spindd-admin

通常運用では使わない。CloudTrail に admin の operator identity が記録されるので、事後に運営者と整合性確認する。

per-tenant Permission Set の権限例

実装は aws/tofu/iam-identity-center.tf (#112)。実際は var.tenants で for_each し、 instance_arn は aws_ssoadmin_instances data source で解決する (下記はイメージ)。SSO Admin API は IdC home region (us-east-1) で叩くため alias provider aws.identity_center を使う。

# aws/tofu/iam-identity-center.tf (実装済み #112、下記は単一テナント展開イメージ)
resource "aws_ssoadmin_permission_set" "tenant_operator_bato" {
  name             = "TenantOperator-bato"
  instance_arn     = "arn:aws:sso:::instance/ssoins-18083f7232cea59f"
  session_duration = "PT8H"   # 8 時間
}

resource "aws_ssoadmin_permission_set_inline_policy" "tenant_operator_bato" {
  instance_arn       = "arn:aws:sso:::instance/ssoins-18083f7232cea59f"
  permission_set_arn = aws_ssoadmin_permission_set.tenant_operator_bato.arn
  inline_policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "ReadSecretsForOwnTenant"
        Effect   = "Allow"
        Action   = ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"]
        Resource = "arn:aws:secretsmanager:ap-northeast-1:*:secret:/automedia/bato/*"
      },
      {
        # routine 更新・チャネル追加用 (セルフサービス credential 投入)
        # CreateSecret は付与しない (= 初回 bootstrap は管理者のみ)
        Sid      = "UpdateSecretsForOwnTenant"
        Effect   = "Allow"
        Action   = ["secretsmanager:PutSecretValue", "secretsmanager:TagResource"]
        Resource = "arn:aws:secretsmanager:ap-northeast-1:*:secret:/automedia/bato/*"
      },
      {
        # 全 read endpoint は tenant を path に持つ (#111) ため、自テナント配下を
        # 1 行の resource scope でまとめて許可でき、他テナントの preview /
        # audience-query を IAM 層で確実に遮断できる。stage は HTTP API の $default。
        Effect   = "Allow"
        Action   = ["execute-api:Invoke"]
        Resource = ["arn:aws:execute-api:ap-northeast-1:*:*/$default/*/api/tenants/bato/*"]
      },
      {
        # Claude Platform on AWS の invoke (`.automedia/bin/claude` launcher 経路)。
        # 現状 workspace は spin-dd 単一なので全テナントで同じ resource を許可する。
        Sid      = "InvokeClaudePlatform"
        Effect   = "Allow"
        Action   = [
          "aws-external-anthropic:CreateInference",
          "aws-external-anthropic:CountTokens",
          "aws-external-anthropic:GetModel",
          "aws-external-anthropic:ListModels",
          "aws-external-anthropic:GetWorkspace",
        ]
        Resource = "arn:aws:aws-external-anthropic:ap-northeast-1:*:workspace/wrkspc_01Jo..."
      },
      {
        # Claude Platform SDK が IAM identity を OIDC token に変換するための STS 権限。
        Sid      = "GetWebIdentityTokenForClaudePlatform"
        Effect   = "Allow"
        Action   = ["sts:GetWebIdentityToken", "sts:TagGetWebIdentityToken"]
        Resource = "arn:aws:sts::*:self"
      }
    ]
  })
}

実装上の補足 (#112):

  • Account Assignment (どの運営者ユーザー/グループをどの Permission Set に割り当てるか) は 組織固有のため、var.operator_assignments (tenant -> [{principal_id, principal_type}]) に Identity Store の GUID を設定して初めて作成される。未設定でも Permission Set 自体は作られる。
  • MFA 必須化は Identity Center のグローバル設定であり aws_ssoadmin_* では管理しない。 Issue #112 の前提どおり console / 組織ポリシーで有効化済みとする。
  • tofu output tenant_operator_permission_set_arns で各 Permission Set ARN を確認できる。

read API への 2 経路は独立しており、混同しないこと: - SSO 運営者: 上記 Permission Set (#112) 自体が execute-api:Invoke (/api/tenants/<t>/*) を持つので、SSO クレデンシャルで read API を直接叩く (別ロールの assume は不要)。Permission Set には secret の get/set (skill 用) も含む。 - テーマリポ GitHub Actions (CI): permission set を持てないため、#111 PR2 の per-tenant read-only role (aws/tofu/iam-tenant-readonly.tf, automedia-readonly-<tenant>) を OIDC で assume して叩く。このロールは execute-api:Invoke のみCI が生 secret に触れないよう Secrets Manager 権限は 意図的に付与しない (生クレデンシャルは read-api Lambda の内側に閉じる)。テーマリポは tofu output tenant_readonly_role_arns の ARN を aws-actions/configure-aws-credentialsrole-to-assume に設定する。

SSO trust (var.operator_sso_role_arns) は既定で空のままでよい。上記のとおり SSO 運営者は Permission Set 直叩きで完結するため、readonly role への SSO trust を有効化する 必要は通常ない。例外的に「OIDC と SSO を単一ロールに集約したい」場合のみ、#112 の AWSReservedSSO ロール ARN を var.operator_sso_role_arns に設定し、かつ Permission Set 側に当該 readonly role への sts:AssumeRole を許可する (両側設定が必要)。接尾辞が Permission Set 再作成で変わるため、その場合は ARN 手書きより data.aws_iam_roles (name_regex) での自動解決を推奨。

権限境界の意図:

  • Read: 自テナントの全 secret を GetSecretValue 可能 (skill が外部 API 呼び出し前に都度取得)
  • Update: 自テナントの既存 secret を PutSecretValue で書き換え可 (routine 更新・チャネル追加時のセルフサービス)
  • Create: CreateSecret は付与しない (新規テナント立ち上げは管理者の bootstrap 経由)
  • Delete: DeleteSecret も付与しない (業務影響大)
  • 他テナント: resource ARN を /automedia/bato/* に限定 → 物理的に遮断

キーの投入とローテーションの役割分担

フェーズ 主体 内容
初回投入 (bootstrap) システム管理者 新テナント立ち上げ時、SNS キー (LINE / Backlog / HubSpot) は管理者が automedia 側でまとめて投入する。鍵の取得が運営者にしかできない場合は、運営者に取得を依頼し 安全な経路 (チャット等) で受領 して管理者が投入する
ローテーション メディア運営者 (セルフサービス) 投入後の定期更新は運営者が /automedia-secret-set で行ってよい。対象は per-tenant の LINE / Backlog / HubSpot
ローテーション (例外) システム管理者のみ GitHub の webhook secret / PAT は /automedia/_meta/github (テナント横断) で管理者管理。運営者は per-tenant secret しか触れない (Permission Set が /automedia/<tenant>/* のみ許可) ため自然に除外される

運営者の Permission Set (#112) は /automedia/<tenant>/*GetSecretValue / PutSecretValue を持つので、LINE / Backlog / HubSpot のローテーションはそのまま可能。_meta/github は対象外なので GitHub 系は管理者のみ。register-saas.mjs の Webhook 登録は「鍵が投入済み」が前提で、未投入 (placeholder) なら early abort する。

ローテーション運用

種別 期限 更新主体 検知方法
AWS SSO セッション 1〜12 時間 (Identity Center 設定) 運営者 (毎日 aws sso login) (期限切れたら CLI が prompt)
IAM Role の STS クレデンシャル SSO セッション内で自動更新 AWS SDK (自動)
Backlog API キー 期限なし → 推奨 90 日 運営者 (skill 経由セルフサービス) secret-watch Lambda の last_updated_at tag 経過日数チェック (90 日 WARN / 120 日 CRITICAL、#114)
HubSpot Service Key 発行時 expires_at あり 運営者 (skill 経由セルフサービス) secret-watch Lambda の expires_at tag 週次チェック (#79)
LINE Channel Access Token 長期 (LINE 側で任意発行) 運営者 (skill 経由セルフサービス) 失効通知 / 月次運用ルール

secret-watch Lambda は expires_at (期限) と last_updated_at (最終更新からの経過) の 2 軸を独立に週次監視する。Backlog 個人 API キーのように発行時に期限が無いものは last_updated_at tag (/automedia-secret-set skill が更新時に付与) の経過日数で 90 日 WARN / 120 日 CRITICAL を判定し、SecretStaleWarning / SecretStaleCritical Custom Metric → CloudWatch Alarm → SNS で通知する。last_updated_at tag が無い secret (placeholder / 未投入) は no-op。

運用ルール (Backlog Wiki にも転記): Backlog 個人 API キーは 90 日サイクルで再発行する。Backlog 個人設定でキーを再発行し、/automedia-secret-set skill で投入する (skill が last_updated_at tag を自動更新する)。120 日経過で CRITICAL Alarm が発火する。

監査

  • CloudTrail: 全 SSO ログイン / IAM Role assume / Secrets Manager GetSecretValue を記録
  • Identity Center のアクセスログ: 誰がいつどの Permission Set でログインしたか
  • Secrets Manager のアクセスログ: secret 取得履歴

不審なアクセスが疑われる場合は Identity Center で当該ユーザーを即時無効化できる。

セキュリティ上の不変条件

  1. 生キーをローカルに保存しない: skill は GetSecretValue で取得後、メモリ内のみで使用。ファイル・環境変数への永続化を避ける
  2. 生キーをログに出さない: skill 実装で console.log / print 出力前にマスク
  3. 書き込み権限は管理者のみ: 運営者の Permission Set は GetSecretValue まで、PutSecretValue は禁止
  4. per-tenant の物理遮断: IAM ポリシーの Resource ARN に /automedia/<tenant>/* を指定
  5. write 操作は本フロー外: LINE 配信 / HubSpot CMS publish 等の副作用ある操作は本フローでは行わない → Backlog 承認経由 (#104) で webhook-backlog Lambda → EventBridge Schedule → deliver Lambda

実装スコープ (このドキュメントが示唆する追加開発)

項目 担当 状態
aws/tofu/iam-identity-center.tf (Permission Set / Account Assignment) automedia 開発者 実装済み (#112, per-tenant TenantOperator-<tenant>。Assignment は var.operator_assignments で注入、MFA は console 管理)
.claude/skills/automedia-aws-config-setup (~/.aws/config の対話的編集、OS 別パス解決 / バックアップ / 重複検出 / list-profiles 検証) 同上 実装済み (#113)
.claude/skills/automedia-init (SSO 状態確認 + AWS_PROFILE 整合検証 + project.json aws.profile 連携) automedia 開発者 (テーマリポ配布) 実装済み (#113)
.claude/skills/automedia-secret-get (Secrets Manager のマスク点検、生キー非表示) 同上 実装済み (#113)
.claude/skills/automedia-secret-set (Secrets Manager 投入ラッパー、echo-off 入力 / JSON スキーマ検証 + 監査タグ) 同上 実装済み (#113)
skill 配布機構 (npx github:spin-dd/automedia init/upgrade) automedia 開発者 実装済み (#113, bin/automedia.mjs。automedia-* のみ冪等コピー)
Claude Platform on AWS launcher (.automedia/bin/claude) automedia 開発者 実装済み (bin/launcher/claude.mjsautomedia.mjs init で配布。詳細は 21-dev-environment.md §Claude Code の 2 つの起動経路)
運営者 Permission Set への aws-external-anthropic:* + sts:GetWebIdentityToken 付与 (launcher を実 API まで通すための前提) automedia 開発者 実装済み (aws/tofu/iam-identity-center.tftenant_operator policy に Lambda 側 (iam.tf) と同等の Statement を追加。tofu apply で in-place 反映、destroy/replace なし)
project.json への aws.profile フィールド追加 + sync Lambda での検証 同上 実装済み (#113, ProjectConfigInvalid metric + Alarm)
secret-watch Lambda の「最終更新からの経過日数」監視拡張 automedia 開発者 実装済み (#114, SecretStaleWarning/Critical Metric + Alarm)
運営者向けオンボーディング手順書 (本ドキュメントを補足) automedia 開発者 (本ドキュメント)
read API Lambda + route (AWS_IAM) の実装 automedia 開発者 実装済み (#111, /api/tenants/<t>/preview|status|audience-query)
テーマリポ用 per-tenant read-only IAM Role (OIDC) automedia 開発者 実装済み (#111, automedia-readonly-<tenant>、execute-api 限定。SSO 運営者は #112 PS 直叩きのため SSO trust は任意・既定不要)
運営者引き継ぎの自動化 Phase 1 (welcome メッセージ生成) automedia 開発者 実装済み (#115, aws/tools/operator-welcome.mjs。値を aws CLI で自動解決)
運営者引き継ぎの自動化 Phase 2 (.claude/skills/automedia-operator-onboard + Identity Center API) automedia 開発者 実装済み (#115, dry-run 既定 / --apply、冪等、distribute:false。権限は管理者 SSO 前提)

別 Issue で段階的に着手する想定。

未決事項

  1. SSO start URL の正式値 — Identity Center に既に設定済の sso_start_url を確認する
  2. Permission Set の session duration — 8h / 12h どちらにするか (Identity Center 既定 vs 運営者 UX)
  3. Backlog 個人キーの集中管理可能性 — 運営者本人が発行するので個人帰属だが、退職時の引き継ぎをどう扱うか
  4. 緊急時のクレデンシャル無効化フロー — 不審アクセス検知時に Identity Center で即時無効化する運用手順

旧未決「複数テナント担当の運営者」は解消済: per-tenant Permission Set を複数 assignment + sso_session 共有 + profile 切替で対応する設計を採用 (SSO config の構造 + 17 §5 テナント切替)。