デモ
このガイドでは、APIを使用してF5 Distributed Cloud上で完全なクライアントサイド防御の演習を実施する手順を説明します。AIアシスタントまたは人間のオペレーターがエンドツーエンドで実行できる4つのフェーズで構成されています。各ステップには、すぐに実行できるcurlコマンドとプレースホルダー値が含まれており、ページ上部のフォーム、.envファイル、または任意の自動化ツールを使用してカスタマイズできます。
演習フェーズ
Section titled “演習フェーズ”| フェーズ | 目標 | ステップ |
|---|---|---|
| フェーズ1 — ビルド | 完全なCSDインフラストラクチャのデプロイと検証 | ステップ1〜7 |
| フェーズ2 — 攻撃 | シミュレートされた攻撃トラフィックの生成とCSDによる検出の確認 | ステップ8〜9 |
| フェーズ3 — 緩和 | 緩和前後の証明 — 攻撃実行、緩和適用、攻撃再実行、比較 | ステップ1〜6 |
| フェーズ4 — ティアダウン | 明示的な確認後にすべてのデプロイオブジェクトを削除 | ティアダウン |
フェーズ1を開始する前に、環境がクリーンであることを確認してください。これらのAPIチェックを実行して、前回の実行から残存するオブジェクトが存在するかどうかを確認します。
# フェーズ1のすべてのオブジェクトを確認し、環境ステータスを計算するHTTP_LB=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/xF5XC_LB_NAMEx-http")HTTPS_LB=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/xF5XC_LB_NAMEx-https")ORIGIN=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools/xF5XC_ORIGIN_POOLx")HC=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks/xF5XC_HC_NAMEx")PD_COUNT=$(curl -s -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains" \ | jq '[.items // [] | .[] | select(.metadata.name != null)] | length')MD_COUNT=$(curl -s -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/mitigated_domains" \ | jq '[.items // [] | .[] | select(.metadata.name != null)] | length')
# HTTPS LBが存在する場合、スケルトン状態を検出するためにボディを取得するHTTPS_IS_SKELETON="false"if [ "$HTTPS_LB" = "200" ]; then HTTPS_LB_BODY=$(curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/xF5XC_LB_NAMEx-https") HTTPS_IS_SKELETON=$(echo "$HTTPS_LB_BODY" | jq ' ((.spec.default_route_pools // []) | length == 0) and (.spec.client_side_defense == null) ')fi
# 決定論的な環境ステータスを計算するjq -n \ --argjson http_lb "$HTTP_LB" \ --argjson https_lb "$HTTPS_LB" \ --argjson origin "$ORIGIN" \ --argjson hc "$HC" \ --argjson pd "$PD_COUNT" \ --argjson md "$MD_COUNT" \ --argjson https_skeleton "$HTTPS_IS_SKELETON" \'{ objects: [ { name: "http_lb", http_code: $http_lb, exists: ($http_lb == 200) }, { name: "https_lb", http_code: $https_lb, exists: ($https_lb == 200), is_skeleton: $https_skeleton }, { name: "origin_pool", http_code: $origin, exists: ($origin == 200) }, { name: "healthcheck", http_code: $hc, exists: ($hc == 200) }, { name: "protected_domains", count: $pd, exists: ($pd > 0) }, { name: "mitigated_domains", count: $md, exists: ($md > 0) } ], any_infra_exists: ($http_lb == 200 or ($https_lb == 200 and ($https_skeleton | not)) or $origin == 200 or $hc == 200), any_csd_exists: ($pd > 0 or $md > 0), status: ( if ($http_lb == 404 and $https_lb == 404 and $origin == 404 and $hc == 404 and $pd == 0 and $md == 0) then "CLEAN" elif ($https_lb == 200 and $https_skeleton and $http_lb == 404 and $origin == 404 and $hc == 404 and $pd == 0 and $md == 0) then "HTTPS_SKELETON" elif ($http_lb == 200 and $origin == 200) then "ALL_EXIST" elif ($http_lb == 200 or ($https_lb == 200 and ($https_skeleton | not)) or $origin == 200 or $hc == 200) then "TEARDOWN_NEEDED" elif ($md > 0 and $http_lb == 404 and ($https_lb == 404 or ($https_lb == 200 and $https_skeleton)) and $origin == 404 and $hc == 404) then "MITIGATIONS_ONLY" else "TEARDOWN_NEEDED" end ), action: ( if ($http_lb == 404 and $https_lb == 404 and $origin == 404 and $hc == 404 and $pd == 0 and $md == 0) then "Proceed to Phase 1" elif ($https_lb == 200 and $https_skeleton and $http_lb == 404 and $origin == 404 and $hc == 404 and $pd == 0 and $md == 0) then "Proceed to Phase 1 (HTTPS LB skeleton will be restored via PUT)" elif ($http_lb == 200 and $origin == 200) then "All Phase 1 objects exist — verify health, optionally skip to Phase 2" elif ($http_lb == 200 or ($https_lb == 200 and ($https_skeleton | not)) or $origin == 200 or $hc == 200) then "Run Phase 4 Teardown first, then re-check" elif ($md > 0 and $http_lb == 404 and ($https_lb == 404 or ($https_lb == 200 and $https_skeleton)) and $origin == 404 and $hc == 404) then "Delete mitigated domains inline, then proceed" else "Run Phase 4 Teardown first, then re-check" end )}'スキップ先の確認
Section titled “スキップ先の確認”すべてのフェーズ1オブジェクトが存在し(200)、フェーズ2にスキップする予定の場合は、フェーズ1 ステップ7の確認コマンドを実行してインフラストラクチャの健全性を確認してからスキップしてください。正確なコマンドはフェーズ1 — ステップ7: 確認を参照してください:
- DNS解決:
dig +short xF5XC_DOMAINNAMEx A - HTTP LBの状態:
GET .../http_loadbalancers/xF5XC_LB_NAMEx-httpをjq '{state: .spec.state}'にパイプ —VIRTUAL_HOST_READYと表示される必要があります - CSD JS設定:
GET .../js_configuration—scriptTagが含まれている必要があります - CSDのステータス:
GET .../statusをjq '{configured: .isConfigured, enabled: .isEnabled}'にパイプ — 両方がtrueである必要があります
フェーズ2にスキップする前に、必要なすべてのチェック(DNS-1、LB-1、CSD-1、CSD-2)がPASSである必要があります。いずれかのチェックが失敗した場合は、失敗したステップからフェーズ1を実行してください。
準備状況の確認マトリックス
Section titled “準備状況の確認マトリックス”上記の事前確認は環境がクリーンであることを確認します。以下の準備状況マトリックスは、環境が準備完了であることを確認します — デモを成功させるために必要なすべての前提条件、クォータ、接続性、およびプラットフォームサービスが整っているかどうかを確認します。準備ステージの一環として、すべてのミーティングの前にこのマトリックスを実行してください。
各チェックには、テストID、ティア(T0〜T5)、PASS/FAIL/WARNの基準、および修復パスがあります。ティアは順次実行されます — 早いティアでのFAILは、後のティアの実行をブロックします。
ティアのサマリー
Section titled “ティアのサマリー”| ティア | カテゴリー | デモをブロックするか? | 目的 |
|---|---|---|---|
| T0 | 接続とデリバリーおよび認証 | はい | プラットフォームに到達して認証できるか? |
| T1 | クォータとキャパシティ | はい(制限に達している場合) | デモオブジェクトを作成する余地があるか? |
| T2 | プラットフォームの前提条件 | はい | テナントレベルのサービスが設定されているか? |
| T3 | オリジンの健全性 | 警告 | バックエンドアプリケーションは応答しているか? |
| T4 | 環境のクリーン状態 | 自動修復 | 前回の実行からの残留オブジェクトがあるか? |
| T5 | 証明書の準備状況 | 情報提供のみ | HTTPSは機能するか、それともHTTPのみで計画すべきか? |
T0: 接続とデリバリーおよび認証
Section titled “T0: 接続とデリバリーおよび認証”これらのチェックは、実行ホストがF5 XC APIに到達でき、認証情報が有効であることを確認します。
PF-T0-1: API接続性
Section titled “PF-T0-1: API接続性”HTTP_CODE=$(curl -s -o /dev/null -w '%\{http_code\}' --connect-timeout 10 --max-time 15 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/web/namespaces")echo "{\"http_code\": $HTTP_CODE}" | jq '{ check: "PF-T0-1", http_code: .http_code, status: ( if .http_code == 200 then "PASS" elif .http_code == 401 then "FAIL" else "FAIL" end ), detail: ( if .http_code == 200 then "API reachable, token valid" elif .http_code == 401 then "Token expired or invalid — regenerate under Administration > Credentials > API Credentials" elif .http_code == 0 then "Network unreachable — check connectivity, VPN, or TLS compatibility (try --tlsv1.2 --tls-max 1.2)" else "Unexpected HTTP \(.http_code)" end )}'PF-T0-2: 名前空間アクセス
Section titled “PF-T0-2: 名前空間アクセス”HTTP_CODE=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers")echo "{\"http_code\": $HTTP_CODE}" | jq '{ check: "PF-T0-2", http_code: .http_code, status: ( if .http_code == 200 then "PASS" elif .http_code == 404 then "WARN" else "FAIL" end ), detail: ( if .http_code == 200 then "Token has namespace access" elif .http_code == 403 then "Token lacks permissions for namespace — check role bindings" elif .http_code == 404 then "Namespace does not exist — will be created in Phase 1 Step 0" else "Unexpected HTTP \(.http_code)" end )}'PF-T0-3: CSD APIアクセス
Section titled “PF-T0-3: CSD APIアクセス”HTTP_CODE=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/status")echo "{\"http_code\": $HTTP_CODE}" | jq '{ check: "PF-T0-3", http_code: .http_code, status: ( if .http_code == 200 then "PASS" elif .http_code == 404 then "WARN" else "FAIL" end ), detail: ( if .http_code == 200 then "Token has CSD/Shape API permissions" elif .http_code == 403 then "Token lacks CSD role binding — contact tenant administrator" elif .http_code == 404 then "Namespace does not exist — CSD access will be verified after namespace creation in Phase 1" else "Unexpected HTTP \(.http_code)" end )}'PF-T0-4: RBACパーミッションマトリックス
Section titled “PF-T0-4: RBACパーミッションマトリックス”非破壊的なプローブは、デモが必要とするすべてのオブジェクトタイプの読み取りおよび書き込み権限をテストします。読み取りはリストエンドポイントへのGETによってテストされます。書き込みは既知の存在しないオブジェクトへのDELETEによってテストされます — RBACが操作を拒否した場合はAPIが403を返し、操作は許可されているがオブジェクトが存在しない場合は404を返します。このゼロ副作用のテクニックにより、一時的なプローブオブジェクトの作成を回避できます。
PROBE_NAME="rbac-probe-nonexistent"
# 読み取りプローブNS_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/web/namespaces/xF5XC_NAMESPACEx")HC_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks")OP_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools")LB_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers")CSD_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/status")PD_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains")MD_R=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/mitigated_domains")
# 書き込みプローブ(非破壊的: 存在しないオブジェクトへのDELETE/cascade_delete)NS_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X POST -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d "{\"name\":\"$PROBE_NAME\"}" \ "xF5XC_API_URLx/api/web/namespaces/$PROBE_NAME/cascade_delete")HC_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks/$PROBE_NAME")OP_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools/$PROBE_NAME")LB_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/$PROBE_NAME")PD_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains/$PROBE_NAME.example.com")MD_W=$(curl -s -o /dev/null -w '%\{http_code\}' --max-time 10 \ -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/mitigated_domains/$PROBE_NAME.example.com")
# 決定論的なパーミッションマトリックスを計算するjq -n \ --argjson ns_r "$NS_R" --argjson ns_w "$NS_W" \ --argjson hc_r "$HC_R" --argjson hc_w "$HC_W" \ --argjson op_r "$OP_R" --argjson op_w "$OP_W" \ --argjson lb_r "$LB_R" --argjson lb_w "$LB_W" \ --argjson csd_r "$CSD_R" \ --argjson pd_r "$PD_R" --argjson pd_w "$PD_W" \ --argjson md_r "$MD_R" --argjson md_w "$MD_W" \'{ check: "PF-T0-4", permissions: [ { object: "namespace", read: ($ns_r != 403), write: ($ns_w != 403), required: false, note: "conditional — only if ns must be created" }, { object: "healthcheck", read: ($hc_r != 403), write: ($hc_w != 403), required: false, note: "optional for CSD" }, { object: "origin_pool", read: ($op_r != 403), write: ($op_w != 403), required: true, note: "" }, { object: "http_loadbalancer", read: ($lb_r != 403), write: ($lb_w != 403), required: true, note: "" }, { object: "csd_status", read: ($csd_r != 403), write: true, required: true, note: "read-only check" }, { object: "protected_domain", read: ($pd_r != 403), write: ($pd_w != 403), required: true, note: "" }, { object: "mitigated_domain", read: ($md_r != 403), write: ($md_w != 403), required: false, note: "Phase 3 only" } ], status: ( if [ ($op_r == 403), ($op_w == 403), ($lb_r == 403), ($lb_w == 403), ($csd_r == 403), ($pd_r == 403), ($pd_w == 403) ] | any then "FAIL" elif ($ns_w == 403 or $hc_w == 403 or $md_w == 403) then "WARN" else "PASS" end ), detail: ( [ (if ($op_r == 403 or $op_w == 403) then "Origin pool: permission denied" else null end), (if ($lb_r == 403 or $lb_w == 403) then "Load balancer: permission denied" else null end), (if $csd_r == 403 then "CSD API: permission denied — CSD may not be enabled for this namespace" else null end), (if ($pd_r == 403 or $pd_w == 403) then "Protected domain: permission denied" else null end), (if $ns_w == 403 then "Namespace: write denied — namespace must already exist (cannot create)" else null end), (if $hc_w == 403 then "Healthcheck: write denied — will skip healthcheck creation" else null end), (if $md_w == 403 then "Mitigated domain: write denied — Phase 3 mitigation will be skipped" else null end) ] | map(select(. != null)) | join("; ") )}'T1: クォータとキャパシティ
Section titled “T1: クォータとキャパシティ”これらのチェックは、テナントのクォータ使用量APIに問い合わせて、デモが必要とする各オブジェクト種別の制限、現在の使用量、および残余キャパシティを確認します。これにより、プローブアンドデリートテストが、正確な数値を報告する単一の読み取り専用API呼び出しに置き換えられます。
PF-T1-0: クォータ使用量ゲート
Section titled “PF-T1-0: クォータ使用量ゲート”テナント全体のクォータ使用量エンドポイントにクエリし、デモが必要とするすべてのオブジェクト種別に対して決定論的なPASS/WARN/FAILステータスを計算します。このエンドポイントはsystem名前空間を必要とします。単一のAPI呼び出しで、すべてのプラットフォームレベルのクォータを一度に確認します。
ゲートは、デモが各オブジェクト種別をいくつ消費するか、その種別が必要かどうか、およびデモを続行するために必要な最小数を指定するdemo_needs配列を定義します。jqフィルターはremainingをneededと比較し、statusフィールドを決定論的に計算します — オペレーターの解釈は不要です。
# ステップ1: クォータデータを取得するcurl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/web/namespaces/system/quota/usage?namespace=system" \ > /tmp/quota.json
# ステップ2: ゲートステータスを計算するjq ' . as $data | [ { kind: "healthcheck", needed: 1, required: false, min_proceed: 0 }, { kind: "origin_pool", needed: 1, required: true, min_proceed: 1 }, { kind: "endpoint", needed: 1, required: true, min_proceed: 1 }, { kind: "http_loadbalancer", needed: 2, required: true, min_proceed: 1 } ] | map( . as $req | $data.objects[$req.kind] as $obj | $obj.limit.maximum as $limit | $obj.usage.current as $usage | (if $limit == -1 then null else ($limit - $usage) end) as $remaining | { kind: $req.kind, limit: (if $limit == -1 then "unlimited" else $limit end), usage: $usage, remaining: (if $remaining == null then "unlimited" else $remaining end), needed: $req.needed, status: ( if $remaining == null then "PASS" elif $remaining >= $req.needed then "PASS" elif $remaining >= $req.min_proceed then "WARN" else (if $req.required then "FAIL" else "WARN" end) end ) } ) | { checks: ., gate: (if any(.[]; .status == "FAIL") then "FAIL" elif any(.[]; .status == "WARN") then "WARN" else "PASS" end) }' /tmp/quota.jsonゲートの出力 — gateフィールドが単一の決定論的な判定です:
PASS— すべてのオブジェクト種別がremaining >= neededを満たしている。デモは続行可能。WARN— 少なくとも1つの種別のキャパシティが削減されているが、続行するための最小限は満たされている(例: LBスロットが2つではなく1つしか利用できない、またはヘルスチェッククォータが枯渇している)。デモは制限付きで続行可能。FAIL— 少なくとも1つの必要な種別がremaining < min_proceedである。クォータが解放されるまでデモは続行できない。
出力例(WARN — エンドポイントが上限に達しており、ヘルスチェックがほぼ満杯):
{ "checks": [ { "kind": "healthcheck", "limit": 150, "usage": 149, "remaining": 1, "needed": 1, "status": "PASS" }, { "kind": "origin_pool", "limit": "unlimited", "usage": 420, "remaining": "unlimited", "needed": 1, "status": "PASS" }, { "kind": "endpoint", "limit": 500, "usage": 500, "remaining": 0, "needed": 1, "status": "FAIL" }, { "kind": "http_loadbalancer", "limit": "unlimited", "usage": 116, "remaining": "unlimited", "needed": 2, "status": "PASS" } ], "gate": "FAIL"}gateの値 | アクション |
|---|---|
| PASS | PF-T1-4(保護ドメインのチェック)に進み、次にT2へ |
| WARN | 準備状況レポートに制限を記載し、機能を縮小して続行 |
| FAIL | 停止 — 枯渇した種別と以下の修復ステップを報告する |
種別ごとの修復方法:
| 種別 | 修復方法 |
|---|---|
healthcheck | 未使用のヘルスチェックを削除してキャパシティを解放する。ヘルスチェックなしでデモは続行できる(CSDは正常性監視を必要としない)。 |
origin_pool | 未使用のオリジンプールを削除するか、管理者にテナント制限の引き上げを依頼する。 |
endpoint | 他の名前空間の未使用オリジンプールを削除してエンドポイントキャパシティを解放する(エンドポイントはオリジンプールのサブオブジェクト)か、管理者に依頼する。 |
http_loadbalancer | 未使用のロードバランサーを削除するか、管理者に依頼する。1スロットのみ利用可能な場合、HTTP LB(プライマリ)は作成されるが、HTTPS LB(セカンダリ)はスキップされる。 |
PF-T1-4: 保護ドメインのクォータ
Section titled “PF-T1-4: 保護ドメインのクォータ”CSDの保護ドメインはプラットフォームのクォータ使用量APIには表示されません。プローブベースのチェックを使用してください: プローブ保護ドメインを作成してすぐに削除します。
# プローブを作成し、HTTPコードとレスポンスボディの両方を取得するPROBE_BODY=$(curl -s -w '\n%\{http_code\}' -X POST \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "name": "preflight-probe.example.com", "namespace": "xF5XC_NAMESPACEx" }, "spec": { "protected_domain": "example.com" } }' \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains")PROBE_HTTP=$(echo "$PROBE_BODY" | tail -1)PROBE_JSON=$(echo "$PROBE_BODY" | sed '$d')
# ステータスを計算するecho "$PROBE_JSON" | jq --argjson http "$PROBE_HTTP" '{ check: "PF-T1-4", http_code: $http, status: ( if $http == 409 then "PASS" elif (.code // 0) == 8 then "FAIL" elif .metadata.name then "PASS" else "FAIL" end ), detail: ( if $http == 409 then "example.com already registered — quota not exhausted" elif (.code // 0) == 8 then "Protected domain quota exhausted — delete unused protected domains" elif .metadata.name then "Probe created — quota available" else "Unexpected response" end )}'
# プローブをクリーンアップする(409が発生した場合は404が予想される)curl -s -X DELETE \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains/preflight-probe.example.com" \ > /dev/nullフォールバック: プローブベースのクォータチェック
Section titled “フォールバック: プローブベースのクォータチェック”PF-T1-0が失敗した場合(クォータ使用量APIが403、404、または予期しない形式を返す場合)、ヘルスチェック、オリジンプール、エンドポイント、およびロードバランサーのクォータについてプローブアンドデリートチェックにフォールバックします。これらのチェックは一時的なオブジェクトを作成してすぐに削除します — 作成時にエラーコード8と”exhausted limits”が返された場合、クォータが満杯です。
各フォールバックプローブは同じパターンを使用します: 一時オブジェクトを作成し、レスポンスから決定論的なステータスを計算し、プローブを削除します。オブジェクトが作成された場合(.metadata.nameが存在する)statusはPASS、エラーコード8(制限枯渇)の場合は種別が必要かどうかに応じてWARNまたはFAILです。
ヘルスチェックプローブ(枯渇した場合はWARN — ヘルスチェックはCSDではオプション):
RESULT=$(curl -s -X POST \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "name": "preflight-quota-probe", "namespace": "xF5XC_NAMESPACEx" }, "spec": { "http_health_check": { "use_origin_server_name": {}, "path": "/", "use_http2": false }, "timeout": 3, "interval": 15, "unhealthy_threshold": 1, "healthy_threshold": 3 } }' \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks")echo "$RESULT" | jq '{ check: "fallback-healthcheck", status: (if .metadata.name then "PASS" elif (.code // 0) == 8 then "WARN" else "FAIL" end), detail: (if .metadata.name then "Quota available" elif (.code // 0) == 8 then "Quota full — healthcheck optional, demo proceeds" else "Unexpected: \(.message // "unknown error")" end)}'curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks/preflight-quota-probe" \ > /dev/nullオリジンプールおよびエンドポイントプローブ(枯渇した場合はFAIL — 両方とも必須):
RESULT=$(curl -s -X POST \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "name": "preflight-origin-probe", "namespace": "xF5XC_NAMESPACEx" }, "spec": { "origin_servers": [{ "public_ip": { "ip": "192.0.2.1" }, "labels": {} }], "no_tls": {}, "port": 80, "same_as_endpoint_port": {}, "healthcheck": [], "loadbalancer_algorithm": "LB_OVERRIDE", "endpoint_selection": "LOCAL_PREFERRED" } }' \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools")echo "$RESULT" | jq '{ check: "fallback-origin-pool", status: (if .metadata.name then "PASS" elif (.code // 0) == 8 then "FAIL" else "FAIL" end), detail: (if .metadata.name then "Quota available" elif (.code // 0) == 8 then "Quota exhausted — \(.message // "limit reached")" else "Unexpected: \(.message // "unknown error")" end)}'curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools/preflight-origin-probe" \ > /dev/nullHTTPロードバランサープローブ(枯渇した場合はFAIL — LBは必須):
RESULT=$(curl -s -X POST \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "name": "preflight-lb-probe", "namespace": "xF5XC_NAMESPACEx", "disable": false }, "spec": { "domains": ["preflight-probe.example.com"], "http": { "dns_volterra_managed": false, "port": 80 }, "advertise_on_public_default_vip": {}, "default_route_pools": [], "disable_rate_limit": {}, "no_service_policies": {}, "round_robin": {}, "disable_waf": {}, "no_challenge": {}, "disable_bot_defense": {}, "disable_api_definition": {}, "disable_api_discovery": {}, "disable_ip_reputation": {}, "disable_malicious_user_detection": {}, "single_lb_app": { "disable_discovery": {}, "disable_ddos_detection": {}, "disable_malicious_user_detection": {} }, "disable_trust_client_ip_headers": {}, "user_id_client_ip": {}, "disable_threat_mesh": {}, "l7_ddos_action_default": {}, "system_default_timeouts": {}, "default_sensitive_data_policy": {}, "disable_malware_protection": {}, "disable_api_testing": {} } }' \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers")echo "$RESULT" | jq '{ check: "fallback-http-lb", status: (if .metadata.name then "PASS" elif (.code // 0) == 8 then "FAIL" else "FAIL" end), detail: (if .metadata.name then "Quota available" elif (.code // 0) == 8 then "Quota exhausted — \(.message // "limit reached")" else "Unexpected: \(.message // "unknown error")" end)}'curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/preflight-lb-probe" \ > /dev/nullT2: プラットフォームの前提条件
Section titled “T2: プラットフォームの前提条件”これらのチェックは、デモが依存するテナントレベルのサービスを確認します。
PF-T2-1: CSDテナントのステータス
Section titled “PF-T2-1: CSDテナントのステータス”curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/status" \ | jq '{ check: "PF-T2-1", configured: .isConfigured, enabled: .isEnabled, status: (if .isConfigured and .isEnabled then "PASS" else "FAIL" end), detail: ( if .isConfigured and .isEnabled then "CSD is active" elif (.isConfigured | not) then "CSD not enabled at tenant level — contact F5 XC administrator" else "CSD configured but not active — contact administrator" end ) }'PF-T2-2: DNSゾーンの存在確認
Section titled “PF-T2-2: DNSゾーンの存在確認”HTTP_CODE=$(curl -s -o /dev/null -w '%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx")echo "{\"http_code\": $HTTP_CODE}" | jq '{ check: "PF-T2-2", http_code: .http_code, status: ( if .http_code == 200 then "PASS" elif .http_code == 404 then "WARN" elif .http_code == 403 then "WARN" else "FAIL" end ), detail: ( if .http_code == 200 then "DNS zone exists in F5 XC" elif .http_code == 404 then "No F5 XC DNS zone — external DNS may be in use" elif .http_code == 403 then "Token lacks DNS zone read access (system namespace)" else "Unexpected HTTP \(.http_code)" end )}'PF-T2-3: DNS管理レコードが有効化されているか
Section titled “PF-T2-3: DNS管理レコードが有効化されているか”PF-T2-2が200を返した場合(F5 XC DNSゾーンが存在する場合)のみ実行してください。
現在の状態を確認する:
curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \ | jq '{ check: "PF-T2-3", managed_records: (.spec.primary.allow_http_lb_managed_records // false), status: (if .spec.primary.allow_http_lb_managed_records == true then "PASS" else "WARN" end), detail: (if .spec.primary.allow_http_lb_managed_records == true then "LB-managed DNS records enabled" else "Managed records not enabled — auto-remediation may be needed" end) }'必要に応じて自動修復する: statusがWARNで、かつPF-T2-4がF5 XCネームサーバー(ns1.f5clouddns.com、ns2.f5clouddns.com)を示している場合は、GET+PUTを使用して管理レコードを自動的に有効化します:
# 現在のゾーン設定を取得するZONE_CONFIG=$(curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx")
# 管理レコードを有効化して更新するecho "$ZONE_CONFIG" \ | jq '.spec.primary.allow_http_lb_managed_records = true' \ | curl -s -X PUT \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ -H "Content-Type: application/json" \ -d @- \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \ | jq .次に再確認して更新が反映されたことを確認します:
curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \ | jq '.spec.primary.allow_http_lb_managed_records'| 結果 | DNSの権威 | ステータス | 修復方法 |
|---|---|---|---|
true | いずれか | PASS | LB管理のDNSレコードが自動作成される |
false/null | F5 XC | 自動修復 | GET+PUTで有効化し、確認して結果を報告する |
false/null | 外部 | 情報 | 外部DNSでは管理レコードは適用されない — フェーズ1 ステップ4ではオプションB(手動レコード作成)を使用する |
| 自動修復が失敗 | F5 XC | FAIL | トークンにシステム名前空間の書き込みアクセスがない可能性がある — テナント管理者に連絡する |
PF-T2-4: DNSネームサーバーの権威
Section titled “PF-T2-4: DNSネームサーバーの権威”NS_RECORDS=$(dig +short NS xF5XC_ROOT_DOMAINx)echo "$NS_RECORDS" | jq -Rs '{ check: "PF-T2-4", nameservers: (split("\n") | map(select(length > 0))), status: ( if (split("\n") | map(select(length > 0)) | length) == 0 then "FAIL" elif test("f5clouddns\\.com") then "PASS" else "INFO" end ), detail: ( if (split("\n") | map(select(length > 0)) | length) == 0 then "No NS records — DNS is broken for this domain" elif test("f5clouddns\\.com") then "F5 XC is authoritative — automatic DNS management available" else "External DNS provider — Phase 1 Step 4 will use Option B (manual record creation)" end )}'T3: オリジンの健全性
Section titled “T3: オリジンの健全性”これらのチェックは、バックエンドアプリケーションが到達可能であることを確認します。
スキップ条件のチェック: 接続テストを実行する前に、オリジンIPがRFC 5737 TEST-NETアドレスかどうかを計算します:
echo "xF5XC_ORIGIN_IPx" | jq -Rs '{ check: "PF-T3-skip", origin_ip: (rtrimstr("\n")), is_test_net: (rtrimstr("\n") | test("^192\\.0\\.2\\.|^198\\.51\\.100\\.|^203\\.0\\.113\\.")), status: (if (rtrimstr("\n") | test("^192\\.0\\.2\\.|^198\\.51\\.100\\.|^203\\.0\\.113\\.")) then "SKIP" else "CONTINUE" end), detail: (if (rtrimstr("\n") | test("^192\\.0\\.2\\.|^198\\.51\\.100\\.|^203\\.0\\.113\\.")) then "RFC 5737 TEST-NET address — not routable, connectivity testing skipped" else "Routable IP — proceed with connectivity tests" end)}'statusがSKIPの場合、PF-T3-1とPF-T3-2をSKIPとして記録し、T4に移動します。それ以外の場合は、以下のチェックを実行します。
PF-T3-1: オリジンサーバーの接続性
Section titled “PF-T3-1: オリジンサーバーの接続性”HTTP_CODE=$(curl -s -o /dev/null -w '%\{http_code\}' --connect-timeout 10 --max-time 15 \ "http://xF5XC_ORIGIN_IPx:xF5XC_ORIGIN_PORTx/")echo "{\"http_code\": $HTTP_CODE}" | jq '{ check: "PF-T3-1", http_code: .http_code, status: (if .http_code >= 200 and .http_code < 600 then "PASS" elif .http_code == 0 then "WARN" else "WARN" end), detail: ( if .http_code >= 200 and .http_code < 600 then "Origin responding with HTTP \(.http_code)" elif .http_code == 0 then "Origin unreachable from this network — LB may use a different path" else "Unexpected response code \(.http_code)" end )}'PF-T3-2: オリジンがHTMLコンテンツを提供しているか
Section titled “PF-T3-2: オリジンがHTMLコンテンツを提供しているか”PF-T3-1が有効なHTTPステータスを返した場合のみ実行してください:
curl -s --max-time 10 "http://xF5XC_ORIGIN_IPx:xF5XC_ORIGIN_PORTx/" \ | grep -qi '</html>' && echo "PASS: HTML content" || echo "WARN: No HTML detected"| 結果 | ステータス | 修復方法 |
|---|---|---|
PASS: HTML content | PASS | オリジンはHTMLページを提供している(CSD JSインジェクションに必要) |
WARN: No HTML detected | WARN | オリジンがAPIのみのサービスまたは非HTMLを返している可能性がある — CSD JSインジェクションにはHTMLページレスポンスが必要 |
T4: 環境のクリーン状態
Section titled “T4: 環境のクリーン状態”これらのチェックは、前回のデモ実行からの名前付きF5 XC設定オブジェクトが残っていないことを確認します — HTTPロードバランサー、HTTPSロードバランサー、オリジンプール、ヘルスチェック、保護ドメイン、および緩和済みドメイン。T4はオブジェクトレベルのクリーンアップに関するものです: フェーズ1の作成と競合するAPIオブジェクトがまだ存在するかどうか。IPアドレス、ネットワーク接続性、またはオリジンの健全性はテストしません(これらの懸念はT3に属します)。
上記の事前確認セクションから6つの事前確認コマンドを実行します。次のステップを決定するために判断ロジックを適用します。オブジェクトが存在する場合、準備ステージ中に自動ティアダウンが実行されます(確認不要)。
また、以前に中断された事前確認の実行からの古いプローブオブジェクトも確認して削除します。これらのプローブは、クォータ使用量APIが利用できずフォールバックプローブベースのチェックが使用された場合、または常にプローブベースのチェックを使用する保護ドメインプローブ(PF-T1-4)に対してのみ作成されます:
# 古いプローブのクリーンアップ(依存関係がないため任意の順序で削除)curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/healthchecks/preflight-quota-probe" \ > /dev/null
curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/preflight-lb-probe" \ > /dev/null
curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/origin_pools/preflight-origin-probe" \ > /dev/null
curl -s -X DELETE -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/shape/csd/namespaces/xF5XC_NAMESPACEx/protected_domains/preflight-probe.example.com" \ > /dev/null各DELETEは、オブジェクトが存在した場合は200(空の{})を返し、存在しなかった場合は404を返します — 両方とも予想される動作です。
T5: 証明書の準備状況
Section titled “T5: 証明書の準備状況”これらのチェックは、HTTPSが機能するか、またはデモをHTTPのみで計画すべきかを評価します。
PF-T5-1: 最近の証明書発行履歴
Section titled “PF-T5-1: 最近の証明書発行履歴”デモドメインに対してLet’s Encrypt証明書が最近発行されたかどうかを確認します。頻繁な作成/破棄サイクルは、週次のレート制限(ドメインごとに週5つの重複証明書)を超える可能性があります。
PF-T5-2: 既存のHTTPS LB証明書の状態
Section titled “PF-T5-2: 既存のHTTPS LB証明書の状態”前回の実行からHTTPS LBが存在する場合のみ実行してください(PF-T4でオブジェクトが見つかった場合):
CERT_BODY=$(curl -s -w '\n%\{http_code\}' \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/namespaces/xF5XC_NAMESPACEx/http_loadbalancers/xF5XC_LB_NAMEx-https")CERT_HTTP=$(echo "$CERT_BODY" | tail -1)CERT_JSON=$(echo "$CERT_BODY" | sed '$d')
if [ "$CERT_HTTP" = "404" ]; then echo '{"check":"PF-T5-2","cert_state":null,"status":"SKIP","detail":"No HTTPS LB — certificate state assessed after Phase 1 Step 3"}'else echo "$CERT_JSON" | jq '{ check: "PF-T5-2", cert_state: .spec.cert_state, status: ( if .spec.cert_state == "CertificateValid" then "PASS" elif .spec.cert_state == "AutoCertDomainRateLimited" then "INFO" elif (.spec.cert_state | test("Pending|Started")) then "INFO" else "INFO" end ), detail: ( if .spec.cert_state == "CertificateValid" then "Certificate healthy — HTTPS will work" elif .spec.cert_state == "AutoCertDomainRateLimited" then "Let'\''s Encrypt rate limit hit — plan for HTTP-only demo" elif (.spec.cert_state | test("Pending|Started")) then "Certificate provisioning in progress" else "Certificate state: \(.spec.cert_state // "unknown")" end ) }'fi準備状況レポートの形式
Section titled “準備状況レポートの形式”すべてのティアを実行した後、統合された準備状況レポートを提示します:
## デモ準備状況: 準備完了 / 準備未完了 / 警告あり準備完了
### T0: 接続とデリバリーおよび認証| チェック | 結果 | ステータス ||---|---|---|| PF-T0-1: API接続性 | 200 | PASS || PF-T0-2: 名前空間アクセス | 200 | PASS || PF-T0-3: CSD APIアクセス | 200 | PASS |
### T1: クォータとキャパシティ| チェック | 種別 | 制限 | 使用量 | 残余 | 必要数 | ステータス ||---|---|---|---|---|---|---|| PF-T1-0: クォータ使用量ゲート | `healthcheck` | 150 | 148 | 2 | 1 | PASS || PF-T1-0: クォータ使用量ゲート | `origin_pool` | unlimited | 420 | unlimited | 1 | PASS || PF-T1-0: クォータ使用量ゲート | `endpoint` | 500 | 498 | 2 | 1 | PASS || PF-T1-0: クォータ使用量ゲート | `http_loadbalancer` | unlimited | 116 | unlimited | 2 | PASS || PF-T1-0: クォータ使用量ゲート | **gate** | — | — | — | — | **PASS** || PF-T1-4: 保護ドメイン | — | — | — | — | 1 | PASS (probe) |
### T2: プラットフォームの前提条件| チェック | 結果 | ステータス ||---|---|---|| PF-T2-1: CSDテナントステータス | configured + enabled | PASS || PF-T2-2: DNSゾーンの存在 | 200 | PASS || PF-T2-3: DNS管理レコード | true | PASS || PF-T2-4: DNSネームサーバーの権威 | f5clouddns.com | PASS |
### T3: オリジンの健全性| チェック | 結果 | ステータス ||---|---|---|| PF-T3-1: オリジン接続性 | TEST-NETアドレス (192.0.2.1) | SKIP || PF-T3-2: HTMLコンテンツ | TEST-NETアドレス | SKIP |
### T4: 環境のクリーン状態| チェック | 結果 | ステータス ||---|---|---|| HTTP LB | 404 | PASS || HTTPS LB | 404 | PASS || オリジンプール | 404 | PASS || ヘルスチェック | 404 | PASS || 保護ドメイン | 0 | PASS || 緩和済みドメイン | 0 | PASS |
### T5: 証明書の準備状況| チェック | 結果 | ステータス ||---|---|---|| PF-T5-2: 証明書状態 | SKIP (HTTPS LBなし) | INFO |
### 警告- (WARNまたはINFOのアイテムをコンテキストとともにリストアップ)総合ステータスのルール:
| 条件 | ステータス |
|---|---|
| T0〜T4のすべてのチェックがPASS | 準備完了 |
| T0〜T4のすべてのチェックがPASSだが、T3またはT5にWARN/INFOがある | 警告あり準備完了 |
| T0、T1、またはT2のいずれかのチェックがFAIL | 準備未完了 — 続行前に解決する |
| T4に残留オブジェクトがある | 自動修復(ティアダウン)し、再確認する |
AIアシスタントの実行プロトコル
Section titled “AIアシスタントの実行プロトコル”このセクションでは、API自動化ステップを実行するAIアシスタント(Claude Code、Copilotなど)向けの決定論的なワークフローを定義します。このプロトコルに従うことで、推測が不要になります — すべての判断ポイントには定義された解決パスがあります。
変数解決プロトコル
Section titled “変数解決プロトコル”以下の正確な順序で各変数を解決します。プレースホルダー以外の値を提供する最初のソースで停止します:
.envファイルを確認する — リポジトリルートの.envを探します。存在する場合は、すべてのKEY=VALUEペアを解析します。- シェル環境を確認する —
env | grep F5XC_を実行して、現在のセッションでエクスポートされた値を見つけます。 - 欠落している値を特定する — 解決された値を以下の必須/オプションの表と比較します。値がない、空、またはまだプレースホルダーのデフォルト(例:
example-api-token、example-tenant、example-namespace、app.example.com、user@example.com)に設定されている場合は「欠落」とみなします。 - 人間のオペレーターに確認を求める — 欠落している必須変数ごとに、オペレーターに値の提供を求めます。すべての必須変数が解決されるまで続行しないでください。
- デフォルトを適用する — 欠落しているオプション変数ごとに、プロンプトなしで以下の表からデフォルトを使用します。
空の文字列 = 欠落: 空の値でエクスポートされた変数 (例:
F5XC_HC_NAME="")は、未設定の変数と同じように扱われます — デフォルトを適用します。シェルのパラメーター展開 (${F5XC_HC_NAME:-csd-hc})を使用して、一ステップでデフォルトを適用します。
- 確認を表示する — 最終的に解決された変数の表をオペレーターに表示し、API呼び出しを実行する前に承認を待ちます。
準備ステージのオーバーライド: ステージ1の準備中は、ステップ6の 待機をスキップします。記録用に解決された変数の表を表示してから、 すぐに進んでください。ステップ1〜5は引き続き適用されます —
.envとシェルを確認した後も必須変数が欠落している場合は、 停止して欠落している変数を報告します。
必須変数とオプション変数
Section titled “必須変数とオプション変数”| 変数 | 必須 | デフォルト | プレースホルダー(欠落として扱う) |
|---|---|---|---|
F5XC_API_TOKEN | はい | — | example-api-token |
F5XC_API_URL | はい | — | https://example-tenant.console.ves.volterra.io |
F5XC_NAMESPACE | はい | — | example-namespace |
F5XC_DOMAINNAME | はい | — | app.example.com |
F5XC_ROOT_DOMAIN | はい | — | example.com |
F5XC_LB_NAME | はい | — | example-lb-name、example-lb |
F5XC_EMAIL | はい | — | user@example.com |
F5XC_HC_NAME | オプション | csd-hc | — |
F5XC_ORIGIN_IP | オプション | 44.232.69.192 | — |
F5XC_ORIGIN_POOL | オプション | csd-origin | — |
F5XC_ORIGIN_PORT | オプション | 3000 | — |
AIアシスタントはデモ中に3つのモードのいずれかで動作します:
| モード | アクティブになる時 | 動作 |
|---|---|---|
| 通常 | 準備、実行、ティアダウン中のデフォルト | ドキュメント記載のコマンドをそのまま使用 |
| デバッグ | 失敗時に自動でアクティブになる | クリエイティブなトラブルシューティング、ドキュメントの更新 |
| Q&A | Q&Aステージ中 | 即興 — アドホックなコマンドを使用して聴衆の質問に回答 |
通常モード(デフォルト):
- すべてのAPI呼び出し、確認クエリ、シェルコマンドはフェーズファイル(フェーズ1〜4)または上記の事前確認セクションからそのまま実行する必要があります
- 解決された変数値で
xTOKENxプレースホルダーのみを置換する - 一般的な知識や推論からAPIエンドポイント、jqフィルター、cURLコマンドを構築しない
- 必要なコマンドがドキュメント化されていない場合は、停止してオペレーターに報告する: 「この確認ステップはフェーズのドキュメントでカバーされていません」
デバッグモード(失敗時に自動でアクティブになる):
- ドキュメント化されたコマンドが予期しない結果を生成した場合に自動的にアクティブになります: 2xx以外のHTTPレスポンス、jqの解析エラー、コマンドのタイムアウト、または証拠テーブルと矛盾するレスポンスボディ
- デバッグモードでは、AIアシスタントは診断コマンドを構築し、生のAPIレスポンスを検査し、エンドポイントのバリエーションをテストし、根本原因を見つけるためのクリエイティブなトラブルシューティングを使用できます
- すべてのデバッグ出力の先頭に
[DEBUG]を付けて、オペレーターが通常の実行から診断アクティビティを区別できるようにします - 学んだことをドキュメント化する: 問題を解決した後、関連するフェーズファイルまたはトラブルシューティングセクションを以下の内容で更新します:
- 失敗シナリオ(何が問題だったか)
- 試みたが機能しなかったこと(将来の実行で繰り返さないように)
- 機能した解決策(問題を解決したコマンドまたは修正)
- デバッグモードの目標は自身を不要にすることです — すべてのデバッグセッションは、次の実行を完全に決定論的にするドキュメントの更新を生成すべきです
- ドキュメントが更新されて問題が解決されたら、通常モードに戻り、最後に成功したドキュメント化されたステップから再開します
- デバッグモードで問題を解決できない場合は、オペレーターに調査結果を報告して停止します — 次のフェーズには続行しません
Q&Aモード(Q&Aステージ中):
- デモ終了後のQ&Aミーティングステージ中のみアクティブです
- AIアシスタントはアドホックなAPI呼び出しを構築し、診断コマンドを実行し、スクリプト化されていないページに移動し、聴衆の質問への回答を説明するためにライブデモ環境を変更できます
[DEBUG]プレフィックスは付けません — これは意図的な即興の動作であり、エラー回復ではありません- 製品に関する質問のナレッジベースとして、
DEMO_EXECUTOR.mdのCSD製品専門知識セクションを使用します
ブラウザコンテキストの管理
Section titled “ブラウザコンテキストの管理”initScriptの蓄積はデモ失敗の一般的な原因です。initScriptパラメーターを含む各navigate_page呼び出しは、以降のすべてのドキュメント読み込みで実行される永続的なリストにスクリプトを追加します。以下のルールに従ってください:
- initScriptを使用した任意のナビゲーションの前に、必ず
about:blankにナビゲートすることで、前回の実行から蓄積されたスクリプトをクリアします - デモフェーズを切り替える際(例: フェーズ2 → フェーズ3)は
isolatedContext付きのnew_pageを使用して、完全にクリーンなブラウザ状態を確保します - 応答しないページからの回復 —
take_screenshotまたはtake_snapshotのタイムアウトが発生した場合、ブラウザコンテキストはリソースが枯渇しています。isolatedContext付きのnew_pageを使用して新鮮なコンテキストを作成し、about:blankナビゲーションステップから再試行します - 一時的なオリジン障害とリソース枯渇の回復についての詳細なガイダンスは、フェーズ2の攻撃シミュレーションのアサイドを参照してください
証拠表示プロトコル
Section titled “証拠表示プロトコル”すべてのAPI呼び出し後、AIアシスタントはこの形式を使用して構造化された証拠を人間のオペレーターに提示する必要があります:
作成ステップ(POST):
| フィールド | 値 | ステータス |
|---|---|---|
| HTTPステータス | 200 | PASS |
| オブジェクト名 | csd-origin | — |
| キープロパティ | (jqで抽出) | — |
各作成ステップの後、GETを実行してオブジェクトが存在することを確認し、その主要なプロパティを表示します。GETが404を返した場合は、FAILと報告して停止します。
確認ステップ(GET/dig):
| テスト | 結果 | ステータス |
|---|---|---|
| DNS-1: Aレコード | 198.51.100.10 | PASS |
| LB-1: HTTP LBの状態 | VIRTUAL_HOST_READY | PASS |
| LB-2: HTTPS LBの状態 | VIRTUAL_HOST_READY | INFO(オプション) |
| TLS-1: 証明書の状態 | CertificateValid | INFO(オプション) |
| CSD-1: JSタグ | scriptTagが存在する | PASS |
各レイヤーの確認標準として、診断と確認のテストケースID(DNS-1、TLS-1、LB-1、CSD-1など)を参照してください。
実行フローのサマリー
Section titled “実行フローのサマリー”フェーズの実行は順次かつゲート制御されています: 各フェーズは次のフェーズが始まる前に、必要なすべてのチェックでPASSに達する必要があります。デモは4段階のミーティングライフサイクルに従います — トリガーフレーズと動作ルールについては、DEMO_EXECUTOR.mdのミーティングステージセクションを参照してください。
AIアシスタントは以下の順序に従います:
- 準備 — 変数を解決し、事前確認チェックを実行し、クリーンな環境を確認する(ミーティング前に別途実行可能)
- イントロダクション — SEが自己紹介し、成果目標を述べる(クライアントサイドの脅威への可視性、PCIコンプライアンス、リアルタイム検出)
- フェーズ1の実行(ステップ1〜7) — インフラストラクチャの作成と確認; すべてのフェーズ1チェックがPASSになるまで次に進まない
- フェーズ2の実行(ステップ8〜9) — 攻撃シミュレーションとAPI経由の検出確認; ブラウザ自動化ツールを持つAIアシスタントはブラウザステップを直接実行し、ブラウザツールを持たないオペレーターは手動で実行する
- フェーズ3の実行 — 検出されたすべてのドメインに対して緩和を適用し、攻撃を再実行し、ブロッキングが有効であることを確認する
- まとめ — 成果目標を再述し、各フェーズからの証拠を要約し、主要な検出と緩和をハイライトする
- Q&A — 即興ステージ、デモはライブのまま、SEが聴衆の質問に回答し、返答の質問をする
- ティアダウン(ミーティング後) — フェーズ4、明示的なオペレーターの確認が必要、依存関係の逆順でオブジェクトをすべて削除し、クリーンな環境を確認する
いずれかのステップがFAILを返した場合は、続行前に失敗と関連するトラブルシューティングセクションのリンクを報告して停止します。
- F5 XC APIトークン — 管理 → 認証情報 → API認証情報の下で生成する
curlとjqがローカルにインストールされていること- ヘルスチェック、オリジンプール、およびHTTPロードバランサーを作成する権限を持つ名前空間
環境のセットアップ
Section titled “環境のセットアップ”環境値を含む.envファイルを作成します。リポジトリにテンプレートが提供されています:
cp .env.example .env実際の値で.envを編集します:
# 必須 — お使いの環境F5XC_API_TOKEN=example-api-tokenF5XC_API_URL=https://example-tenant.console.ves.volterra.ioF5XC_DOMAINNAME=app.example.comF5XC_EMAIL=user@example.comF5XC_LB_NAME=example-lb-nameF5XC_NAMESPACE=example-namespaceF5XC_ROOT_DOMAIN=example.com
# オプション — 設定されていない場合はJuice Shopデモのデフォルトが使用される# F5XC_HC_NAME=csd-hc# F5XC_ORIGIN_IP=44.232.69.192# F5XC_ORIGIN_POOL=csd-origin# F5XC_ORIGIN_PORT=3000ファイルをソースしてシェルセッションに変数を読み込みます:
set -a && source .env && set +acurlコマンド内の各xTOKENxプレースホルダーは環境変数に直接マッピングされます — 例えば、xF5XC_API_TOKENxは$F5XC_API_TOKENに対応します。ページ上部のインタラクティブフォームを使用してこれらの値を置換するか、Claude CodeなどのAIアシスタントに.envを読み込んでコマンドを構築させることができます。
プレースホルダートークン
Section titled “プレースホルダートークン”| トークン | デフォルト | 説明 |
|---|---|---|
xF5XC_API_URLx | https://example-tenant.console.ves.volterra.io | XC コンソールAPI URL |
xF5XC_API_TOKENx | example-api-token | API認証情報トークン |
xF5XC_EMAILx | user@example.com | CSD通知メールアドレス |
xF5XC_NAMESPACEx | example-namespace | 名前空間 |
xF5XC_LB_NAMEx | example-lb | HTTPロードバランサーのベース名(${name}-httpと${name}-httpsを作成) |
xF5XC_DOMAINNAMEx | app.example.com | 保護するFQDN |
xF5XC_ROOT_DOMAINx | example.com | CSD保護ドメインのルートドメイン(eTLD+1) |
xF5XC_ORIGIN_POOLx | csd-origin | オリジンプール名 |
xF5XC_ORIGIN_IPx | 44.232.69.192 | オリジンサーバーIP |
xF5XC_ORIGIN_PORTx | 3000 | オリジンサーバーポート |
xF5XC_HC_NAMEx | csd-hc | ヘルスチェック名 |
自動化リファレンス
Section titled “自動化リファレンス”このセクションでは、スクリプトや自動化のための完全な演習ワークフローをまとめます。
クイックスタート
Section titled “クイックスタート”- リポジトリをクローンして環境テンプレートをコピーする:
cp .env.example .env - テナントURL、APIトークン、名前空間、およびドメイン値で
.envを編集する - 環境をソースする:
set -a && source .env && set +a - 各フェーズを順番に実行し、次のフェーズに進む前に各証拠ブロックでPASSを確認する
値はAIアシスタントの実行プロトコルで定義された決定論的なプロトコルを使用して解決されます:
.envファイル — リポジトリルートからKEY=VALUEペアを解析する- シェル環境 — エクスポートされた値のために
env | grep F5XC_を確認する - プレースホルダーの検出 — プレースホルダーのデフォルト(例:
example-api-token、example-namespace)に一致する値を欠落としてフラグを立てる - オペレーターへの確認 — 欠落している各必須変数の値を求める
- デフォルトの適用 — 欠落しているオプション変数に組み込みのデフォルトを使用する
- 確認 — 解決された変数の表を表示し、オペレーターの承認を待つ
- フェーズ1 — ビルド: インフラストラクチャのデプロイ(ヘルスチェック、オリジンプール、HTTP LB + HTTPS LB)、DNSの設定、CSDの有効化、保護ドメインの登録、すべてのコンポーネントの確認。HTTP LBがプライマリデモターゲット; HTTPS LBはオプション。
- フェーズ2 — 攻撃:
http://URLを使用してブラウザで攻撃シミュレーションを実行し、5〜10分待ち、API(/scripts、/detected_domains、/formFields)で検出を確認する - フェーズ3 — 緩和: クリーンなベースラインを確認し、攻撃を実行(前の証明)、各ドメインを
/mitigated_domainsにPOSTし、緩和が適用されたことを確認し、http://URLを使用して攻撃を再実行(後の証明)、前後の比較を提示する - フェーズ4 — ティアダウン (明示的な人間の確認が必要): HTTPS LBを削除 → HTTP LBを削除 → オリジンプールを削除 → DNSゾーンのクリーンアップ(手動レコードのみ)→ ヘルスチェックを削除 → 保護ドメインを削除。DNSゾーンは削除しない。
| トークン | 説明 | デフォルト |
|---|---|---|
xF5XC_API_URLx | XC コンソールAPI URL | https://example-tenant.console.ves.volterra.io |
xF5XC_API_TOKENx | API認証情報トークン | (ユーザー提供) |
xF5XC_EMAILx | CSD通知メール | user@example.com |
xF5XC_NAMESPACEx | 名前空間 | example-namespace |
xF5XC_LB_NAMEx | HTTPロードバランサーのベース名(${name}-httpと${name}-httpsを作成) | example-lb |
xF5XC_DOMAINNAMEx | 保護するFQDN | app.example.com |
xF5XC_ROOT_DOMAINx | ルートドメイン(eTLD+1) | example.com |
xF5XC_ORIGIN_POOLx | オリジンプール名 | csd-origin |
xF5XC_ORIGIN_IPx | オリジンサーバーIP | 44.232.69.192 |
xF5XC_ORIGIN_PORTx | オリジンサーバーポート | 3000 |
xF5XC_HC_NAMEx | ヘルスチェック名 | csd-hc |
oneOfチョイスグループ
Section titled “oneOfチョイスグループ”HTTPロードバランサーのspecは、グループごとに正確に1つのオプションを設定する必要があるoneOfチョイスグループを使用します。グループ内でゼロまたは複数のオプションを設定すると422エラーが発生します。
CSD関連の主要なチョイス:
| チョイスグループ | オプション | CSDのデフォルト |
|---|---|---|
client_side_defense_choice | client_side_defense、disable_client_side_defense | client_side_defense |
java_script_choice(CSD内にネスト) | disable_js_insert、js_insert_all_pages、js_insert_all_pages_except、js_insertion_rules | js_insert_all_pages |
リスナータイプのチョイス(HTTP対HTTPS):
デモは異なるリスナー設定を持つ2つのLBを作成します:
| LB | リスナーチョイス | 設定 |
|---|---|---|
${F5XC_LB_NAME}-http(プライマリ) | http | "http": { "dns_volterra_managed": true, "port": 80 } |
${F5XC_LB_NAME}-https(セカンダリ) | https_auto_cert | "https_auto_cert": { "http_redirect": true, "port": 443, ... } |
httpとhttps_auto_certキーは相互に排他的です — 各LBは正確に1つを使用します。
HTTPSオートサートのネストされたチョイス(セカンダリLBのみ):
| チョイスグループ | オプション | デフォルト |
|---|---|---|
| ポート | port(数値) | 443 |
server_header_choice | default_header、server_name、append_server_name | default_header |
path_normalize_choice | enable_path_normalize、disable_path_normalize | enable_path_normalize |
mtls_choice | no_mtls、use_mtls | no_mtls |
default_loadbalancer_choice | default_loadbalancer、non_default_loadbalancer | default_loadbalancer |
single_lb_appのネストされたチョイス(ML設定):
single_lb_appオブジェクトには独自の必須oneOfグループがあります。これらのネストされたチョイスなしにsingle_lb_app: \{\}を設定すると400エラーが発生します。
| チョイスグループ | オプション | デフォルト |
|---|---|---|
api_discovery_choice | disable_discovery、enable_discovery | disable_discovery |
ddos_detection_choice | disable_ddos_detection、enable_ddos_detection | disable_ddos_detection |
malicious_user_detection_choice | disable_malicious_user_detection、enable_malicious_user_detection | disable_malicious_user_detection |
その他のLBレベルのチョイス(すべて無効/デフォルトに設定):
disable_rate_limit · no_service_policies · round_robin · disable_waf · no_challenge · disable_bot_defense · disable_api_definition · disable_api_discovery · disable_ip_reputation · disable_malicious_user_detection · single_lb_app · disable_trust_client_ip_headers · user_id_client_ip · disable_threat_mesh · l7_ddos_action_default · system_default_timeouts · default_sensitive_data_policy · disable_malware_protection · disable_api_testing
エラーハンドリング
Section titled “エラーハンドリング”- 401 Unauthorized — APIトークンが無効または期限切れです。管理 → 認証情報で再生成してください。
- 403 Forbidden — トークンに名前空間の権限がありません。ロールバインディングを確認してください。
- 404 Not Found — 名前空間またはオブジェクト名が正しくありません。
GET /api/config/namespaces/\{namespace\}/\{object_type\}でオブジェクトをリストしてください。 - 409 Conflict — オブジェクトがすでに存在します。保護ドメインの場合、「domain already exists (in uriList)」の
409はルートドメインがすでにテナントに登録されていることを意味します — これは成功条件であり、エラーではありません。その他のオブジェクトの場合は、最初に削除するかPUTを使用して更新してください。 - 422 Unprocessable Entity — JSONスキーマの検証に失敗しました。一般的な原因:
oneOfチョイスの欠落、同じグループで複数のチョイスが設定されている、フィールドの型が間違っている。特定のフィールドについてはエラーメッセージを確認してください。 - オブジェクト制限枯渇(エラーコード
8、メッセージ"Object kind {kind} has exhausted limits({N})") — テナントがオブジェクトクォータ制限に達しました。APIはHTTP 429ではなく、"code": 8を含むJSONエラーボディを持つHTTP 200を返します。このエラーに遭遇する前に、クォータ使用量API(GET /api/web/namespaces/system/quota/usage?namespace=system)を使用してクォータキャパシティを事前に確認できます。動作はオブジェクトタイプによって異なります:- ヘルスチェック(制限約150): 非ブロッキング — フェーズ1 ステップ1をスキップして、ヘルスチェック参照なしでオリジンプールを作成します。CSDは正常性監視に依存しません。
- エンドポイント(制限約500): ブロッキング — エンドポイントはオリジンプール内に作成されるサブオブジェクトです。この制限に達した場合、オリジンプールの作成が失敗します。他の名前空間の未使用オリジンプールを削除して(エンドポイントサブオブジェクトも解放される)、またはテナント制限の引き上げを管理者に依頼してください。
- オリジンプール: ブロッキング — 未使用のオリジンプールを削除するか、管理者に依頼してください。
- HTTPロードバランサー: ブロッキング — 未使用のロードバランサーを削除するか、管理者に依頼してください。
- 保護ドメイン: ブロッキング — 未使用の保護ドメインを削除するか、管理者に依頼してください。
トラブルシューティング
Section titled “トラブルシューティング”ヘルスチェックがオリジンプールにリンクされていない
Section titled “ヘルスチェックがオリジンプールにリンクされていない”フェーズ1 ステップ2(ヘルスチェックのリンクを確認)で空の配列[]が表示される場合:
- オリジンプールを削除する:
DELETE /api/config/namespaces/{namespace}/origin_pools/{pool_name} - ヘルスチェックが存在することを確認する:
GET /api/config/namespaces/{namespace}/healthchecks/{hc_name} - 正しいヘルスチェック参照でオリジンプールを再作成する
LBがVIRTUAL_HOST_PENDING_A_RECORDのままになっている
Section titled “LBがVIRTUAL_HOST_PENDING_A_RECORDのままになっている”フェーズ1 ステップ4後もロードバランサーのstateがVIRTUAL_HOST_PENDING_A_RECORDのままの場合:
-
DNSゾーンの存在を確認する(F5 XC管理DNSのみ):
Terminal window curl -s \-H "Authorization: APIToken xF5XC_API_TOKENx" \"xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \| jq '.metadata.name'404はF5 XCでDNSゾーンが作成されていないことを意味します。 -
allow_http_lb_managed_recordsを確認する — ゾーンが存在するがLBが保留中の場合、管理レコードが無効になっている可能性があります:Terminal window curl -s \-H "Authorization: APIToken xF5XC_API_TOKENx" \"xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \| jq '.spec.primary.allow_http_lb_managed_records'falseまたはnullの場合は、フェーズ1 ステップ4、オプションAのPUTコマンドを使用して有効化してください。 -
外部DNS — F5 XCが権威を持たない場合は、DNSプロバイダーで手動でAレコードとACME CNAMEレコードを作成してください(フェーズ1 ステップ4、オプションBを参照)。
-
解決の確認 — DNS修正後、以下を確認します:
Terminal window dig +short xF5XC_DOMAINNAMEx AAレコードが解決されれば、LBは
VIRTUAL_HOST_READYに移行します。30秒ごとにポーリングし、最大4回(合計2分)実行します。2分後もまだVIRTUAL_HOST_PENDING_A_RECORDの場合は、DNSの伝播を再確認してオペレーターに報告してください。
CSDのステータスがisConfigured: falseを示している
Section titled “CSDのステータスがisConfigured: falseを示している”CSDはテナントレベルで有効化する必要があります。テナントのクライアントサイド防御を有効化するには、F5 XC管理者に連絡してください。これはAPIで設定できないテナント全体の設定です。
JSインジェクションが機能していない
Section titled “JSインジェクションが機能していない”- テナントレベルでCSDが有効になっていることを確認する(フェーズ1 ステップ5)
- ロードバランサーのspecに
client_side_defenseが設定されていることを確認する - JS設定エンドポイントが
scriptTagを返すことを確認する - ブラウザで保護されたドメインにアクセスし、ページソースを表示してスクリプトがインジェクトされていることを確認する
保護ドメインの登録が409を返す
Section titled “保護ドメインの登録が409を返す”「domain already exists (in uriList)」の409は、ルートドメインがすでにテナントに登録されていることを意味します。保護ドメインはテナントスコープ — 単一の名前空間に属しません。これは成功条件です: ドメインはすでに保護されており、追加のアクションは不要です。フェーズ1 ステップ7に進んでください。
AutoCertDomainRateLimited
Section titled “AutoCertDomainRateLimited”HTTPS LBのcert_stateがAutoCertDomainRateLimitedを示している場合、Let’s Encryptがこのドメインの証明書発行のレート制限を設けたことを意味します。これはインフラストラクチャが頻繁に作成および破棄されるデモ環境では予想される動作です。
影響: レート制限がリセットされる(通常1時間)まで、HTTPS LBはトラフィックを提供しません。HTTP LBは完全に影響を受けません — すべてのデモトラフィックはhttp://で正常に続行されます。
解決策: デモの進行には何もアクションは不要です。HTTP LB(${F5XC_LB_NAME}-http)がプライマリデモターゲットであり、証明書のプロビジョニングに依存しません。HTTPSが必要な場合は、レート制限がリセットされるのを待てば証明書が自動的にプロビジョニングされます。
証明書がスタック — クリーンな再作成(オプション)
Section titled “証明書がスタック — クリーンな再作成(オプション)”HTTPS LBの証明書が15分以上DomainChallengePendingまたはPreDomainChallengePendingでスタックしている場合、最も早い回復パスはHTTPS LBを削除して再作成することです。DNSゾーンがすでに設定されている(管理レコードが有効)状態で、クリーンな再作成は通常5〜7分でCertificateValidを生成します — レート制限がない場合。
- HTTPSロードバランサーを削除する:
DELETE .../http_loadbalancers/${F5XC_LB_NAME}-https - プラットフォームのクリーンアップのために30秒待つ
- HTTPSロードバランサーを再作成する(フェーズ1 ステップ3)
- 証明書の状態を監視する(フェーズ1 ステップ7)— 5〜10分以内に
CertificateValidになることを期待する
OpenAPIリファレンス
Section titled “OpenAPIリファレンス”F5 Distributed CloudのAPIの標準仕様は以下から入手できます:
https://docs.cloud.f5.com/docs-v2/downloads/f5-distributed-cloud-open-api.zip
このZIPには、ves.io.schema.views.http_loadbalancer、ves.io.schema.healthcheck、ves.io.schema.origin_pool、およびves.io.schema.shape.csdを含むすべてのAPIグループのOpenAPI 3.0 specが含まれています。これらのspecを使用してJSONペイロードを検証し、追加フィールドを確認してください。