데모
이 가이드는 API를 사용하여 F5 Distributed Cloud에서 완전한 Client-Side Defense 실습을 진행하는 과정을 안내합니다 — AI 어시스턴트 또는 인간 운영자가 처음부터 끝까지 실행할 수 있도록 4단계로 구성되어 있습니다. 각 단계에는 페이지 상단의 폼, .env 파일, 또는 자동화 도구를 사용하여 사용자 정의할 수 있는 플레이스홀더 값이 포함된 즉시 실행 가능한 curl 명령이 포함되어 있습니다.
실습 단계
섹션 제목: “실습 단계”| 단계 | 목표 | 스텝 |
|---|---|---|
| 1단계 — 구축 | 전체 CSD 인프라 배포 및 검증 | 스텝 1–7 |
| 2단계 — 공격 | 시뮬레이션된 공격 트래픽 생성 및 CSD 탐지 확인 | 스텝 8–9 |
| 3단계 — 완화 | 완화 전/후 증명 — 공격 실행, 완화 적용, 공격 재실행, 비교 | 스텝 1–6 |
| 4단계 — 해제 | 명시적 확인 후 모든 배포 객체 제거 | 해제 |
사전 점검
섹션 제목: “사전 점검”1단계를 시작하기 전에 환경이 깨끗한지 확인합니다. 이전 실행에서 남은 객체가 있는지 확인하려면 다음 API 점검을 실행하십시오:
# Check all Phase 1 objects and compute environment statusHTTP_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')
# If HTTPS LB exists, fetch body to detect skeleton stateHTTPS_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
# Compute deterministic environment statusjq -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 )}'건너뛰기 검증
섹션 제목: “건너뛰기 검증”모든 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)가 통과해야 합니다. 검사가 실패하면 실패한 스텝부터 1단계를 실행하십시오.
준비 상태 검증 매트릭스
섹션 제목: “준비 상태 검증 매트릭스”위의 사전 점검은 환경이 깨끗한지 확인합니다. 아래의 준비 상태 매트릭스는 환경이 가능한 상태인지 — 성공적인 데모를 위한 모든 전제 조건, 할당량, 연결성 및 플랫폼 서비스가 갖추어져 있는지 확인합니다. 준비 단계의 일부로 매 미팅 전에 이 매트릭스를 실행하십시오.
각 검사에는 테스트 ID, 계층(T0–T5), 통과/실패/경고 기준 및 해결 경로가 있습니다. 계층은 순차적입니다 — 이전 계층의 실패는 이후 계층의 실행을 차단합니다.
계층 요약
섹션 제목: “계층 요약”| 계층 | 카테고리 | 데모 차단? | 목적 |
|---|---|---|---|
| T0 | 연결성 및 인증 | 예 | 플랫폼에 접속하고 인증할 수 있는가? |
| T1 | 할당량 및 용량 | 예 (한도 도달 시) | 데모 객체를 생성할 여유가 있는가? |
| T2 | 플랫폼 전제 조건 | 예 | 테넌트 수준 서비스가 구성되어 있는가? |
| T3 | 오리진 상태 | 경고 | 백엔드 애플리케이션이 응답하는가? |
| T4 | 환경 정리 | 자동 해결 | 이전 실행에서 남은 객체가 있는가? |
| T5 | 인증서 준비 상태 | 정보 제공 | HTTPS가 작동할 것인가, HTTP 전용으로 계획해야 하는가? |
T0: 연결성 및 인증
섹션 제목: “T0: 연결성 및 인증”이 검사들은 실행 호스트가 F5 XC API에 접속할 수 있고 자격 증명이 유효한지 확인합니다.
PF-T0-1: API 연결성
섹션 제목: “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: 네임스페이스 접근
섹션 제목: “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 접근
섹션 제목: “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 권한 매트릭스
섹션 제목: “PF-T0-4: RBAC 권한 매트릭스”비파괴적 프로브로 데모에 필요한 모든 객체 유형에 대한 읽기 및 쓰기 권한을 테스트합니다. 읽기는 목록 엔드포인트의 GET으로 테스트합니다. 쓰기는 존재하지 않는 것으로 알려진 객체에 대한 DELETE로 테스트합니다 — RBAC가 작업을 거부하면 API가 403을 반환하고, 작업이 허용되지만 객체가 존재하지 않으면 404를 반환합니다. 이 부작용 없는 기법은 임시 프로브 객체 생성을 방지합니다.
PROBE_NAME="rbac-probe-nonexistent"
# Read probesNS_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")
# Write probes (non-destructive: DELETE/cascade_delete on non-existent objects)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")
# Compute deterministic permission matrixjq -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: 할당량 및 용량
섹션 제목: “T1: 할당량 및 용량”이 검사들은 테넌트의 할당량 사용 API를 조회하여 데모에 필요한 각 객체 종류의 한도, 현재 사용량 및 잔여 용량을 확인합니다. 프로브 후 삭제 테스트를 정확한 수치를 보고하는 단일 읽기 전용 API 호출로 대체합니다.
PF-T1-0: 할당량 사용 게이트
섹션 제목: “PF-T1-0: 할당량 사용 게이트”테넌트 전체 할당량 사용 엔드포인트를 조회하고 데모에 필요한 모든 객체 종류에 대해 결정적 통과/경고/실패 상태를 계산합니다. 이 엔드포인트는 system 네임스페이스가 필요합니다. 단일 API 호출로 모든 플랫폼 수준 할당량을 한 번에 확인합니다.
게이트는 데모가 소비할 각 객체 종류의 수, 해당 종류가 필수인지 여부, 데모 진행에 필요한 최소 수를 지정하는 demo_needs 배열을 정의합니다. jq 필터는 remaining과 needed를 비교하고 status 필드를 결정적으로 계산합니다 — 운영자 해석이 필요하지 않습니다.
# Step 1: Fetch quota datacurl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/web/namespaces/system/quota/usage?namespace=system" \ > /tmp/quota.json
# Step 2: Compute gate statusjq ' . 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— 하나 이상의 종류가 용량이 줄었지만 진행에 필요한 최소치는 충족됩니다(예: 2개가 아닌 1개의 LB 슬롯만 사용 가능, 또는 헬스체크 할당량 소진). 데모는 제한 사항과 함께 진행할 수 있습니다.FAIL— 하나 이상의 필수 종류가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: 보호 도메인 할당량
섹션 제목: “PF-T1-4: 보호 도메인 할당량”CSD 보호 도메인은 플랫폼 할당량 사용 API에 표시되지 않습니다. 프로브 기반 검사를 사용합니다: 프로브 보호 도메인을 생성하고 즉시 삭제합니다.
# Create probe and capture both HTTP code and response bodyPROBE_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')
# Compute statusecho "$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 )}'
# Cleanup probe (404 is expected if 409 occurred)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대체: 프로브 기반 할당량 검사
섹션 제목: “대체: 프로브 기반 할당량 검사”PF-T1-0이 실패하는 경우(할당량 사용 API가 403, 404 또는 예상치 못한 형식을 반환), 헬스체크, 오리진 풀, 엔드포인트 및 로드 밸런서 할당량에 대한 프로브 후 삭제 검사로 대체합니다. 이 검사들은 임시 객체를 생성하고 즉시 삭제합니다 — 생성 시 오류 코드 8과 “exhausted limits”가 반환되면 할당량이 가득 찬 것입니다.
각 대체 프로브는 동일한 패턴을 사용합니다: 임시 객체를 생성하고 응답에서 결정적 상태를 계산한 다음 프로브를 삭제합니다. status 필드는 객체가 생성되면(.metadata.name 존재) 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: 플랫폼 전제 조건
섹션 제목: “T2: 플랫폼 전제 조건”이 검사들은 데모가 의존하는 테넌트 수준 서비스를 확인합니다.
PF-T2-1: CSD 테넌트 상태
섹션 제목: “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 존 존재 여부
섹션 제목: “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 관리 레코드 활성화 여부
섹션 제목: “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을 사용하여 관리 레코드를 자동 활성화합니다:
# Get current zone configZONE_CONFIG=$(curl -s \ -H "Authorization: APIToken xF5XC_API_TOKENx" \ "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx")
# Update with managed records enabledecho "$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 | 모든 경우 | 통과 | LB 관리 DNS 레코드가 자동 생성됩니다 |
false/null | F5 XC | 자동 해결 | GET+PUT으로 활성화, 확인, 결과 보고 |
false/null | 외부 | 정보 | 외부 DNS에는 관리 레코드가 적용되지 않음 — 1단계 스텝 4에서 옵션 B(수동 레코드 생성)를 사용합니다 |
| 자동 해결 실패 | F5 XC | 실패 | 토큰에 system 네임스페이스 쓰기 접근 권한이 없을 수 있음 — 테넌트 관리자에게 문의 |
PF-T2-4: DNS 네임서버 권한
섹션 제목: “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: 오리진 상태
섹션 제목: “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: 오리진 서버 연결성
섹션 제목: “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 콘텐츠 제공 여부
섹션 제목: “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 | 통과 | 오리진이 HTML 페이지를 제공합니다(CSD JS 삽입에 필요) |
WARN: No HTML detected | 경고 | 오리진이 API 전용 서비스이거나 비HTML을 반환할 수 있음 — CSD JS 삽입에는 HTML 페이지 응답이 필요합니다 |
T4: 환경 정리
섹션 제목: “T4: 환경 정리”이 검사들은 이전 데모 실행에서 명명된 F5 XC 구성 객체가 남아 있지 않은지 확인합니다 — HTTP 로드 밸런서, HTTPS 로드 밸런서, 오리진 풀, 헬스체크, 보호 도메인 및 완화 도메인. T4는 객체 수준 정리에 관한 것입니다: 1단계 생성과 충돌할 수 있는 API 객체가 여전히 존재하는지 여부입니다. IP 주소, 네트워크 연결성 또는 오리진 상태는 테스트하지 않습니다(해당 사항은 T3에 해당).
위의 사전 점검 섹션에서 6개의 사전 점검 명령을 실행합니다. 판단 로직을 적용하여 다음 단계를 결정합니다. 객체가 존재하는 경우, 준비 단계에서 자동 해제가 수행됩니다(확인 필요 없음).
또한 이전에 중단된 사전 점검 실행에서 남은 오래된 프로브 객체를 확인하고 삭제합니다. 이러한 프로브는 할당량 사용 API를 사용할 수 없어 대체 프로브 기반 검사가 사용된 경우, 또는 항상 프로브 기반 검사를 사용하는 보호 도메인 프로브(PF-T1-4)의 경우에만 생성됩니다:
# Stale probe cleanup (delete in any order — probes have no dependencies)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: 인증서 준비 상태
섹션 제목: “T5: 인증서 준비 상태”이 검사들은 HTTPS가 작동할지 또는 데모가 HTTP 전용으로 계획해야 하는지 평가합니다.
PF-T5-1: 최근 인증서 발급 이력
섹션 제목: “PF-T5-1: 최근 인증서 발급 이력”데모 도메인에 대해 최근 Let’s Encrypt 인증서가 발급되었는지 확인합니다. 빈번한 생성/삭제 주기는 주간 속도 제한(도메인당 주당 5개의 중복 인증서)을 소진할 수 있습니다.
PF-T5-2: 기존 HTTPS LB 인증서 상태
섹션 제목: “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준비 상태 보고서 형식
섹션 제목: “준비 상태 보고서 형식”모든 계층을 실행한 후, 통합 준비 상태 보고서를 제시합니다:
## 데모 준비 상태: READY / NOT READY / READY WITH WARNINGS
### 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 address (192.0.2.1) | SKIP || PF-T3-2: HTML 콘텐츠 | TEST-NET address | SKIP |
### T4: 환경 정리| 검사 | 결과 | 상태 ||---|---|---|| HTTP LB | 404 | PASS || HTTPS LB | 404 | PASS || Origin Pool | 404 | PASS || Healthcheck | 404 | PASS || Protected Domains | 0 | PASS || Mitigated Domains | 0 | PASS |
### T5: 인증서 준비 상태| 검사 | 결과 | 상태 ||---|---|---|| PF-T5-2: 인증서 상태 | SKIP (no HTTPS LB) | INFO |
### 경고- (컨텍스트와 함께 WARN 또는 INFO 항목 나열)전체 상태 규칙:
| 조건 | 상태 |
|---|---|
| 모든 T0–T4 검사 통과 | READY |
| 모든 T0–T4 검사 통과하지만 T3 또는 T5에 WARN/INFO 있음 | READY WITH WARNINGS |
| T0, T1 또는 T2 검사 중 하나라도 FAIL | NOT READY — 진행 전 해결 필요 |
| T4에 남은 객체 있음 | 자동 해결(해제) 후 재확인 |
AI 어시스턴트 실행 프로토콜
섹션 제목: “AI 어시스턴트 실행 프로토콜”이 섹션은 API 자동화 단계를 실행하는 AI 어시스턴트(Claude Code, Copilot 등)를 위한 결정적 워크플로우를 정의합니다. 이 프로토콜을 따르면 추측이 제거됩니다 — 모든 의사 결정 지점에 정의된 해결 경로가 있습니다.
변수 확인 프로토콜
섹션 제목: “변수 확인 프로토콜”각 변수를 정확히 이 순서로 확인합니다. 비플레이스홀더 값을 제공하는 첫 번째 소스에서 멈춥니다:
.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및 셸을 확인한 후에도 필수 변수가 누락된 경우 중지하고 누락된 변수를 보고합니다.
필수 및 선택 변수
섹션 제목: “필수 및 선택 변수”| 변수 | 필수 | 기본값 | 플레이스홀더 (누락으로 처리) |
|---|---|---|---|
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 어시스턴트는 데모 중 세 가지 모드 중 하나로 작동합니다:
| 모드 | 활성화 시점 | 동작 |
|---|---|---|
| 일반 | 준비, 실행, 해제 중 기본 | 문서화된 명령만 그대로 실행 |
| 디버그 | 실패 시 자동 활성화 | 창의적 문제 해결, 문서 업데이트 |
| 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 제품 전문 지식 섹션을 제품 질문의 지식 기반으로 사용합니다
브라우저 컨텍스트 관리
섹션 제목: “브라우저 컨텍스트 관리”initScript 누적은 데모 실패의 일반적인 원인입니다. initScript
매개변수가 있는 각 navigate_page 호출은 이후의 모든 문서 로드에서
실행되는 영구 목록에 스크립트를 추가합니다. 다음 규칙을 따르십시오:
initScript가 있는 탐색 전에 항상about:blank로 이동하여 이전 실행에서 누적된 스크립트를 지웁니다- 데모 단계 간(예: 2단계 → 3단계) 전환 시
new_page와isolatedContext를 사용하여 완전히 깨끗한 브라우저 상태를 보장합니다 - 응답 없는 페이지 복구 —
take_screenshot또는take_snapshot시간 초과가 발생하면 브라우저 컨텍스트가 리소스 소진 상태입니다.new_page와isolatedContext를 사용하여 새 컨텍스트를 생성한 다음about:blank탐색 단계에서 재시도합니다 - 일시적 오리진 장애 및 리소스 소진 복구에 대한 자세한 안내는 2단계 공격 시뮬레이션 참고 사항을 참조하십시오
증거 표시 프로토콜
섹션 제목: “증거 표시 프로토콜”모든 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 등)를 참조합니다.
실행 흐름 요약
섹션 제목: “실행 흐름 요약”단계 실행은 순차적이며 게이트됩니다: 다음 단계가 시작되기 전에 각 단계가 모든 필수 검사에서 통과해야 합니다. 데모는 4단계 미팅 수명 주기를 따릅니다 — 트리거 구문 및 동작 규칙은 DEMO_EXECUTOR.md의 미팅 단계 섹션을 참조하십시오.
AI 어시스턴트는 이 순서를 따릅니다:
- 준비 — 변수 확인, 사전 점검 실행, 깨끗한 환경 확인 (미팅 전 별도로 실행 가능)
- 소개 — SE가 자신을 소개하고 결과 목표를 명시합니다 (클라이언트 측 위협에 대한 가시성, PCI 준수, 실시간 탐지)
- 1단계 실행 (스텝 1–7) — 인프라 생성 및 검증; 진행하기 전에 모든 1단계 검사가 통과해야 합니다
- 2단계 실행 (스텝 8–9) — API를 통한 공격 시뮬레이션 및 탐지 검증; 브라우저 자동화가 있는 AI 어시스턴트는 브라우저 단계를 직접 실행하고, 브라우저 도구가 없는 운영자는 수동으로 수행합니다
- 3단계 실행 — 탐지된 모든 도메인에 대한 완화 적용, 공격 재실행, 차단이 효과적인지 검증
- 결론 — 결과 목표 재진술, 각 단계의 증거 요약, 주요 탐지 및 완화 강조
- Q&A — 즉흥 단계, 데모는 라이브로 유지, SE가 청중 질문에 답하고 역질문을 합니다
- 해제 (미팅 후) — 4단계, 명시적 운영자 확인 필요, 역 의존성 순서로 모든 객체 삭제, 깨끗한 환경 확인
어떤 단계라도 FAIL을 반환하면, 계속하기 전에 관련 문제 해결 섹션 링크와 함께 실패를 보고하고 중지합니다.
전제 조건
섹션 제목: “전제 조건”- F5 XC API 토큰 — Administration → Credentials → API Credentials에서 생성합니다
- 로컬에
curl및jq설치 - 헬스체크, 오리진 풀, HTTP 로드 밸런서를 생성할 권한이 있는 네임스페이스
환경 설정
섹션 제목: “환경 설정”환경 값으로 .env 파일을 생성합니다. 저장소에 템플릿이 제공됩니다:
cp .env.example .env실제 값으로 .env를 편집합니다:
# Required — your environmentF5XC_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
# Optional — Juice Shop demo defaults are used when not set# 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를 읽고 명령을 구성하도록 할 수 있습니다.
플레이스홀더 토큰
섹션 제목: “플레이스홀더 토큰”| 토큰 | 기본값 | 설명 |
|---|---|---|
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 | 헬스체크 이름 |
자동화 참조
섹션 제목: “자동화 참조”이 섹션은 스크립팅 또는 자동화를 위한 전체 실습 워크플로우를 요약합니다.
빠른 시작
섹션 제목: “빠른 시작”- 저장소를 클론하고 환경 템플릿을 복사합니다:
cp .env.example .env - 테넌트 URL, API 토큰, 네임스페이스 및 도메인 값으로
.env를 편집합니다 - 환경을 소싱합니다:
set -a && source .env && set +a - 각 단계를 순서대로 실행하며, 다음 단계로 진행하기 전에 각 증거 블록에서 통과를 확인합니다
변수 확인
섹션 제목: “변수 확인”값은 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 선택 그룹
섹션 제목: “oneOf 선택 그룹”HTTP 로드 밸런서 스펙은 그룹당 정확히 하나의 옵션을 설정해야 하는 oneOf 선택 그룹을 사용합니다. 그룹에서 0개 또는 2개 이상의 옵션을 설정하면 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 vs HTTPS):
데모는 서로 다른 리스너 구성으로 두 개의 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는 정확히 하나만 사용합니다.
HTTPS 자동 인증서 중첩 선택 (보조 LB만):
| 선택 그룹 | 옵션 | 기본값 |
|---|---|---|
| port | 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
오류 처리
섹션 제목: “오류 처리”- 401 Unauthorized — API 토큰이 유효하지 않거나 만료되었습니다. Administration → Credentials에서 재생성하십시오.
- 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)를 사용하여 이 오류를 만나기 전에 사전에 할당량 용량을 확인할 수 있습니다. 동작은 객체 유형에 따라 다릅니다:- Healthcheck (한도 ~150): 비차단 — 1단계 스텝 1을 건너뛰고 헬스체크 참조 없이 오리진 풀을 생성합니다. CSD는 상태 모니터링에 의존하지 않습니다.
- Endpoint (한도 ~500): 차단 — 엔드포인트는 오리진 풀 내부에 생성되는 하위 객체입니다. 이 한도에 도달하면 오리진 풀 생성이 실패합니다. 사용하지 않는 오리진 풀을 삭제하거나(엔드포인트 하위 객체가 해제됨) 관리자에게 테넌트 한도 증가를 요청하십시오.
- Origin pool: 차단 — 사용하지 않는 오리진 풀을 삭제하거나 관리자에게 문의하십시오.
- HTTP load balancer: 차단 — 사용하지 않는 로드 밸런서를 삭제하거나 관리자에게 문의하십시오.
- Protected domain: 차단 — 사용하지 않는 보호 도메인을 삭제하거나 관리자에게 문의하십시오.
문제 해결
섹션 제목: “문제 해결”헬스체크가 오리진 풀에 연결되지 않음
섹션 제목: “헬스체크가 오리진 풀에 연결되지 않음”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 상태에서 멈춤
섹션 제목: “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를 표시
섹션 제목: “CSD 상태가 isConfigured: false를 표시”CSD는 테넌트 수준에서 활성화해야 합니다. 테넌트에 대해 Client-Side Defense를 활성화하려면 F5 XC 관리자에게 문의하십시오. 이는 API를 통해 구성할 수 없는 테넌트 전체 설정입니다.
JS 삽입이 작동하지 않음
섹션 제목: “JS 삽입이 작동하지 않음”- 테넌트 수준에서 CSD가 활성화되어 있는지 확인합니다(1단계 스텝 5)
- 로드 밸런서 스펙에
client_side_defense가 설정되어 있는지 확인합니다 - JS 구성 엔드포인트가
scriptTag를 반환하는지 확인합니다 - 브라우저에서 보호 도메인을 방문하고 페이지 소스를 확인하여 스크립트가 삽입되었는지 확인합니다
보호 도메인 등록이 409를 반환
섹션 제목: “보호 도메인 등록이 409를 반환”“domain already exists (in uriList)“가 포함된 409는 루트 도메인이 테넌트에 이미 등록되어 있음을 의미합니다. 보호 도메인은 테넌트 범위입니다 — 특정 네임스페이스에 속하지 않습니다. 이는 성공 조건입니다: 도메인이 이미 보호되고 있으며 추가 조치가 필요하지 않습니다. 1단계 스텝 7로 계속하십시오.
AutoCertDomainRateLimited
섹션 제목: “AutoCertDomainRateLimited”HTTPS LB cert_state가 AutoCertDomainRateLimited를 표시하면, 이 도메인에 대한 Let’s Encrypt 인증서 발급이 속도 제한되었음을 의미합니다. 이는 인프라가 자주 생성 및 삭제되는 데모 환경에서 예상되는 동작입니다.
영향: 속도 제한이 재설정될 때까지(일반적으로 1시간) HTTPS LB가 트래픽을 제공하지 않습니다. HTTP LB는 완전히 영향받지 않습니다 — 모든 데모 트래픽은 http://를 통해 정상적으로 진행됩니다.
해결: 데모 진행을 위한 조치가 필요하지 않습니다. HTTP LB(${F5XC_LB_NAME}-http)가 기본 데모 대상이며 인증서 프로비저닝에 의존하지 않습니다. HTTPS가 필요한 경우, 속도 제한이 재설정될 때까지 기다리면 인증서가 자동 프로비저닝됩니다.
인증서 멈춤 — 깨끗한 재생성 (선택 사항)
섹션 제목: “인증서 멈춤 — 깨끗한 재생성 (선택 사항)”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 참조
섹션 제목: “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 스펙이 포함되어 있습니다. 이 스펙을 사용하여 JSON 페이로드를 검증하고 추가 필드를 확인하십시오.