クレデンシャル配布 (テーマリポ 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-watchLambda (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-setskill の挙動) のみを扱う。
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 spindd1 回で全 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 1 の operator_welcome_<tenant> output で生成され、管理者から運営者へ伝達される。
/automedia-secret-set skill の内部挙動¶
運営者が /automedia-secret-set provider=<...> を実行した時の処理:
--tenant引数 (将来project.json > tenant) とAWS_PROFILEから secret ARN を解決 (/automedia/<tenant>/<provider>)- 既存値を
GetSecretValueで取得 (差分確認用、生キーは画面表示しない) - 入力プロンプトで新値を受け取る (
process.stdin、echo off で伏字) - JSON スキーマで構造検証 (例: LINE なら
channel_id/channel_secret/access_token必須) PutSecretValueで更新- 更新者 (
CallerIdentity) と日時をtag: last_updated_by,last_updated_atで記録 - 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-initskill はproject.json > aws.profileを読み、AWS_PROFILE未設定ならexport AWS_PROFILE=<profile>を案内 + その profile で検証する (サブプロセスは親シェルの env を変更できないため「自動設定」ではなく「検証 + 案内」)。aws.profileは client 側関心で deliver は runtime で使わない。sync Lambda は project.json 同期時にaws.profile/channelsの欠落を検証し、欠落時は CloudWatch メトリクスautomedia/Sync > ProjectConfigInvalid→ Alarm で通知する (S3 put 自体は ブロックしない)。
システム管理者の責務¶
初回オンボーディング (運営者 1 名追加)¶
詳細手順は 14a §9 運営者への引き継ぎ案内 を参照。要点 (運営者ごとに毎回実施):
/automedia-operator-onboard --send-invite --applyで運営者ユーザー作成 + Permission Set (TenantOperator-<tenant>) 割当 + SES 招待メール送信を 1 コマンドで完遂 (#115 / #174 / #188)- 運営者は届いた招待メールに従って初期パスワード設定 + MFA 登録 (Authenticator アプリ等)
- 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 (= 運営者がセルフサービスできるための前提作業)¶
- Secrets Manager に placeholder secret を作成 (
automedia-secret-watchのexpires_attag も初期値を仮設定):→ コードは placeholder 値 (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"}' ..._placeholderprefix) を検出して該当 provider の処理を no-op にする (既存パターン、webhook-lineLambda の HubSpot 同期 skip と同じ) - Permission Set に PutSecretValue を
/automedia/<tenant>/*限定で付与 (詳細は下の OpenTofu 例) - 運営者へ「以後の credential 投入は
/automedia-secret-setskill を使ってください」と案内
管理者にしかできない対応¶
| 操作 | 理由 |
|---|---|
| 新規テナント 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-credentialsのrole-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-setskill で投入する (skill がlast_updated_attag を自動更新する)。120 日経過で CRITICAL Alarm が発火する。
監査¶
- CloudTrail: 全 SSO ログイン / IAM Role assume / Secrets Manager
GetSecretValueを記録 - Identity Center のアクセスログ: 誰がいつどの Permission Set でログインしたか
- Secrets Manager のアクセスログ: secret 取得履歴
不審なアクセスが疑われる場合は Identity Center で当該ユーザーを即時無効化できる。
セキュリティ上の不変条件¶
- 生キーをローカルに保存しない: skill は GetSecretValue で取得後、メモリ内のみで使用。ファイル・環境変数への永続化を避ける
- 生キーをログに出さない: skill 実装で console.log / print 出力前にマスク
- 書き込み権限は管理者のみ: 運営者の Permission Set は
GetSecretValueまで、PutSecretValueは禁止 - per-tenant の物理遮断: IAM ポリシーの
ResourceARN に/automedia/<tenant>/*を指定 - 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.mjs → automedia.mjs init で配布。詳細は 21-dev-environment.md §Claude Code の 2 つの起動経路) |
運営者 Permission Set への aws-external-anthropic:* + sts:GetWebIdentityToken 付与 (launcher を実 API まで通すための前提) |
automedia 開発者 | 実装済み (aws/tofu/iam-identity-center.tf の tenant_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 で段階的に着手する想定。
未決事項¶
- SSO start URL の正式値 — Identity Center に既に設定済の
sso_start_urlを確認する - Permission Set の session duration — 8h / 12h どちらにするか (Identity Center 既定 vs 運営者 UX)
- Backlog 個人キーの集中管理可能性 — 運営者本人が発行するので個人帰属だが、退職時の引き継ぎをどう扱うか
- 緊急時のクレデンシャル無効化フロー — 不審アクセス検知時に Identity Center で即時無効化する運用手順
旧未決「複数テナント担当の運営者」は解消済: per-tenant Permission Set を複数 assignment +
sso_session共有 + profile 切替で対応する設計を採用 (SSO config の構造 + 17 §5 テナント切替)。