Pular para o conteúdo

Demonstração

Este guia orienta você por um exercício completo de Client-Side Defense no F5 Distributed Cloud usando a API — estruturado em quatro fases que um assistente de IA ou operador humano pode executar de ponta a ponta. Cada etapa inclui um comando curl pronto para execução com valores de placeholder que você pode personalizar usando o formulário no topo da página, um arquivo .env ou qualquer ferramenta de automação.

FaseObjetivoEtapas
Fase 1 — ConstruirImplantar e validar toda a infraestrutura CSDEtapas 1–7
Fase 2 — AtacarGerar tráfego de ataque simulado e confirmar que o CSD o detectouEtapas 8–9
Fase 3 — MitigarProva antes/depois da mitigação — executar ataque, aplicar mitigações, re-executar ataque, compararEtapas 1–6
Fase 4 — DesmontarRemover todos os objetos de implantação após confirmação explícitaDesmontagem

Antes de iniciar a Fase 1, verifique se o ambiente está limpo. Execute estas verificações de API para determinar se existem objetos remanescentes de uma execução anterior:

Terminal window
# Check all Phase 1 objects and compute environment status
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')
# If HTTPS LB exists, fetch body to detect skeleton state
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
# Compute deterministic environment status
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
)
}'

Quando todos os objetos da Fase 1 existem (200) e você planeja pular para a Fase 2, execute os comandos de verificação da Etapa 7 da Fase 1 para confirmar a saúde da infraestrutura antes de pular. Use os comandos exatos de Fase 1 — Etapa 7: Verificar:

  1. Resolução DNS: dig +short xF5XC_DOMAINNAMEx A
  2. Estado do HTTP LB: GET .../http_loadbalancers/xF5XC_LB_NAMEx-http direcionado para jq '{state: .spec.state}' — deve mostrar VIRTUAL_HOST_READY
  3. Configuração JS do CSD: GET .../js_configuration — deve conter scriptTag
  4. Status do CSD: GET .../status direcionado para jq '{configured: .isConfigured, enabled: .isEnabled}' — ambos devem ser true

Todas as verificações obrigatórias (DNS-1, LB-1, CSD-1, CSD-2) devem PASSAR antes de pular para a Fase 2. Se alguma verificação falhar, execute a Fase 1 a partir da etapa que falhou.

A verificação pré-voo acima verifica se o ambiente está limpo. A matriz de prontidão abaixo verifica se o ambiente é capaz — que todos os pré-requisitos, cotas, conectividade e serviços de plataforma estão configurados para uma demonstração bem-sucedida. Execute esta matriz antes de cada reunião como parte do estágio de Preparação.

Cada verificação tem um ID de teste, um nível (T0–T5), critérios PASS/FAIL/WARN e um caminho de remediação. Os níveis são sequenciais — um FAIL em um nível anterior bloqueia os níveis posteriores de serem executados.

NívelCategoriaBloqueia a Demo?Propósito
T0Conectividade e AutenticaçãoSimConseguimos alcançar a plataforma e autenticar?
T1Cotas e CapacidadeSim (se no limite)Há espaço para criar objetos da demo?
T2Pré-requisitos da PlataformaSimOs serviços em nível de tenant estão configurados?
T3Saúde da OrigemAvisoA aplicação backend está respondendo?
T4Ambiente LimpoAuto-remediarHá objetos remanescentes de uma execução anterior?
T5Prontidão de CertificadoInformativoO HTTPS funcionará ou devemos planejar apenas HTTP?

Estas verificações confirmam que o host de execução pode alcançar a API do F5 XC e que as credenciais são válidas.

Terminal window
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
)
}'
Terminal window
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
)
}'
Terminal window
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
)
}'

Sondas não destrutivas testam permissões de leitura e escrita para cada tipo de objeto que a demo necessita. A leitura é testada via GET em endpoints de listagem. A escrita é testada via DELETE em objetos reconhecidamente inexistentes — a API retorna 403 se o RBAC nega a operação, ou 404 se a operação é permitida mas o objeto não existe. Esta técnica com zero efeitos colaterais evita a criação de objetos de sondagem temporários.

Terminal window
PROBE_NAME="rbac-probe-nonexistent"
# Read probes
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")
# 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 matrix
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("; ")
)
}'

Estas verificações consultam a API de Uso de Cotas do tenant para determinar limites, uso atual e capacidade restante para cada tipo de objeto que a demo necessita. Isso substitui testes de sondagem e exclusão por uma única chamada de API somente leitura que reporta números exatos.

Consulte o endpoint de uso de cotas em nível de tenant e compute um status determinístico PASS/WARN/FAIL para cada tipo de objeto que a demo necessita. Este endpoint requer o namespace system. Uma única chamada de API verifica todas as cotas em nível de plataforma de uma vez.

O portão define um array demo_needs que especifica quantos de cada tipo de objeto a demo consumirá, se o tipo é obrigatório e o mínimo necessário para a demo prosseguir. O filtro jq compara remaining contra needed e computa o campo status deterministicamente — nenhuma interpretação do operador é necessária.

Terminal window
# Step 1: Fetch quota data
curl -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 status
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

Saída do portão — o campo gate é o veredito único determinístico:

  • PASS — todos os tipos de objeto têm remaining >= needed. A demo pode prosseguir.
  • WARN — pelo menos um tipo tem capacidade reduzida mas o mínimo para prosseguir é atendido (ex.: apenas 1 slot de LB disponível em vez de 2, ou cota de healthcheck esgotada). A demo pode prosseguir com limitações.
  • FAIL — pelo menos um tipo obrigatório tem remaining < min_proceed. A demo não pode prosseguir até que cotas sejam liberadas.

Exemplo de saída (WARN — endpoint no limite, healthcheck quase cheio):

{
"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"
}
Valor de gateAção
PASSProssiga para PF-T1-4 (verificação de domínio protegido), depois T2
WARNAnote as limitações no relatório de prontidão, prossiga com capacidade reduzida
FAILPare — reporte quais tipos estão esgotados e as etapas de remediação abaixo

Remediação por tipo:

TipoRemediação
healthcheckExclua healthchecks não utilizados para liberar capacidade. A demo prossegue sem healthcheck (CSD não requer um).
origin_poolExclua origin pools não utilizados ou entre em contato com seu administrador para aumentar o limite do tenant.
endpointExclua origin pools não utilizados em outros namespaces para liberar capacidade de endpoints (endpoints são sub-objetos de origin pools), ou entre em contato com seu administrador.
http_loadbalancerExclua load balancers não utilizados ou entre em contato com seu administrador. Se apenas 1 slot estiver disponível, o HTTP LB (primário) será criado mas o HTTPS LB (secundário) será pulado.

Domínios protegidos do CSD não aparecem na API de Uso de Cotas da plataforma. Use uma verificação baseada em sondagem: crie e imediatamente exclua um domínio protegido de sondagem.

Terminal window
# Create probe and capture both HTTP code and response body
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')
# Compute status
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
)
}'
# 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

Alternativa: Verificações de Cotas Baseadas em Sondagem

Seção intitulada “Alternativa: Verificações de Cotas Baseadas em Sondagem”

Se PF-T1-0 falhar (a API de Uso de Cotas retorna 403, 404 ou um formato inesperado), utilize verificações alternativas de sondagem e exclusão para cotas de healthcheck, origin pool, endpoint e load balancer. Estas verificações criam um objeto temporário e o excluem imediatamente — se a criação retornar código de erro 8 com “exhausted limits”, a cota está cheia.

Cada sondagem alternativa usa o mesmo padrão: criar um objeto temporário, computar um status determinístico a partir da resposta, depois excluir a sondagem. O campo status é PASS se o objeto foi criado (.metadata.name presente), WARN ou FAIL se código de erro 8 (limites esgotados), dependendo se o tipo é obrigatório.

Sondagem de healthcheck (WARN se esgotado — healthchecks são opcionais para CSD):

Terminal window
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

Sondagem de origin pool e endpoint (FAIL se esgotado — ambos são obrigatórios):

Terminal window
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/null

Sondagem de HTTP load balancer (FAIL se esgotado — LBs são obrigatórios):

Terminal window
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/null

Estas verificações validam serviços em nível de tenant dos quais a demo depende.

Terminal window
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
)
}'
Terminal window
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
)
}'

Execute apenas se PF-T2-2 retornou 200 (zona DNS do F5 XC existe).

Verificar estado atual:

Terminal window
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)
}'

Auto-remediar se necessário: Se o status for WARN E PF-T2-4 mostrar nameservers do F5 XC (ns1.f5clouddns.com, ns2.f5clouddns.com), habilite automaticamente os registros gerenciados usando GET+PUT:

Terminal window
# Get current zone config
ZONE_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 enabled
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 .

Depois verifique novamente para confirmar que a atualização teve efeito:

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'
ResultadoAutoridade DNSStatusRemediação
trueQualquerPASSRegistros DNS gerenciados pelo LB serão auto-criados
false/nullF5 XCAuto-remediarHabilitar via GET+PUT, verificar, reportar resultado
false/nullExternoINFORegistros gerenciados não aplicáveis para DNS externo — Fase 1 Etapa 4 usará Opção B (criação manual de registro)
Auto-remediação falhouF5 XCFAILToken pode não ter acesso de escrita ao namespace system — entre em contato com o administrador do tenant
Terminal window
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
)
}'

Estas verificações validam se a aplicação backend está acessível.

Verificação de condição de pular: Compute se o IP de origem é um endereço TEST-NET RFC 5737 antes de executar testes de conectividade:

Terminal window
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)
}'

Se status for SKIP, registre PF-T3-1 e PF-T3-2 como SKIP e prossiga para T4. Caso contrário, execute as verificações abaixo.

Terminal window
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
)
}'

Execute apenas se PF-T3-1 retornou um status HTTP válido:

Terminal window
curl -s --max-time 10 "http://xF5XC_ORIGIN_IPx:xF5XC_ORIGIN_PORTx/" \
| grep -qi '</html>' && echo "PASS: HTML content" || echo "WARN: No HTML detected"
ResultadoStatusRemediação
PASS: HTML contentPASSOrigem serve páginas HTML (necessário para injeção de JS do CSD)
WARN: No HTML detectedWARNOrigem pode ser um serviço apenas API ou retornando não-HTML — a injeção de JS do CSD requer respostas de página HTML

Estas verificações validam que nenhum objeto de configuração nomeado do F5 XC permanece de uma execução anterior da demo — HTTP load balancers, HTTPS load balancers, origin pools, healthchecks, domínios protegidos e domínios mitigados. T4 trata de limpeza em nível de objetos: se objetos de API que conflitariam com a criação da Fase 1 ainda existem. Não testa endereços IP, conectividade de rede ou saúde da origem (essas preocupações pertencem ao T3).

Execute os seis comandos de pré-voo da seção Verificação Pré-voo acima. Aplique a Lógica de Decisão para determinar os próximos passos. Se objetos existirem, a auto-desmontagem é realizada durante o estágio de Preparação (sem necessidade de confirmação).

Também verifique e exclua objetos de sondagem obsoletos de execuções de pré-voo anteriores interrompidas. Estas sondagens são criadas apenas quando a API de Uso de Cotas está indisponível e as verificações alternativas baseadas em sondagem foram usadas, ou para a sondagem de domínio protegido (PF-T1-4) que sempre usa verificação baseada em sondagem:

Terminal window
# 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

Cada DELETE retorna 200 ({} vazio) se o objeto existia, ou 404 se não — ambos são esperados.

Estas verificações avaliam se o HTTPS funcionará ou se a demo deve planejar apenas HTTP.

PF-T5-1: Histórico Recente de Emissão de Certificado

Seção intitulada “PF-T5-1: Histórico Recente de Emissão de Certificado”

Verifique se um certificado Let’s Encrypt foi emitido recentemente para o domínio da demo. Ciclos frequentes de criação/destruição podem esgotar o limite semanal de taxa (5 certificados duplicados por semana por domínio).

PF-T5-2: Estado do Certificado do HTTPS LB Existente

Seção intitulada “PF-T5-2: Estado do Certificado do HTTPS LB Existente”

Execute apenas se um HTTPS LB existe de uma execução anterior (PF-T4 encontrou objetos):

Terminal window
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

Após executar todos os níveis, apresente um relatório de prontidão consolidado:

## Prontidão da Demo: PRONTO / NÃO PRONTO / PRONTO COM AVISOS
### T0: Conectividade e Autenticação
| Verificação | Resultado | Status |
|---|---|---|
| PF-T0-1: Conectividade da API | 200 | PASS |
| PF-T0-2: Acesso ao Namespace | 200 | PASS |
| PF-T0-3: Acesso à API do CSD | 200 | PASS |
### T1: Cotas e Capacidade
| Verificação | Tipo | Limite | Uso | Restante | Necessário | Status |
|---|---|---|---|---|---|---|
| PF-T1-0: Portão de Uso de Cotas | `healthcheck` | 150 | 148 | 2 | 1 | PASS |
| PF-T1-0: Portão de Uso de Cotas | `origin_pool` | unlimited | 420 | unlimited | 1 | PASS |
| PF-T1-0: Portão de Uso de Cotas | `endpoint` | 500 | 498 | 2 | 1 | PASS |
| PF-T1-0: Portão de Uso de Cotas | `http_loadbalancer` | unlimited | 116 | unlimited | 2 | PASS |
| PF-T1-0: Portão de Uso de Cotas | **portão** | — | — | — | — | **PASS** |
| PF-T1-4: Domínio Protegido | — | — | — | — | 1 | PASS (sondagem) |
### T2: Pré-requisitos da Plataforma
| Verificação | Resultado | Status |
|---|---|---|
| PF-T2-1: Status do Tenant CSD | configurado + habilitado | PASS |
| PF-T2-2: Zona DNS Existe | 200 | PASS |
| PF-T2-3: Registros DNS Gerenciados | true | PASS |
| PF-T2-4: Autoridade de Nameserver DNS | f5clouddns.com | PASS |
### T3: Saúde da Origem
| Verificação | Resultado | Status |
|---|---|---|
| PF-T3-1: Conectividade da Origem | Endereço TEST-NET (192.0.2.1) | SKIP |
| PF-T3-2: Conteúdo HTML | Endereço TEST-NET | SKIP |
### T4: Ambiente Limpo
| Verificação | Resultado | Status |
|---|---|---|
| HTTP LB | 404 | PASS |
| HTTPS LB | 404 | PASS |
| Origin Pool | 404 | PASS |
| Healthcheck | 404 | PASS |
| Domínios Protegidos | 0 | PASS |
| Domínios Mitigados | 0 | PASS |
### T5: Prontidão de Certificado
| Verificação | Resultado | Status |
|---|---|---|
| PF-T5-2: Estado do Cert | SKIP (sem HTTPS LB) | INFO |
### Avisos
- (liste quaisquer itens WARN ou INFO com contexto)

Regras de status geral:

CondiçãoStatus
Todas as verificações T0–T4 PASSPRONTO
Todas as verificações T0–T4 PASS mas T3 ou T5 têm WARN/INFOPRONTO COM AVISOS
Qualquer verificação T0, T1 ou T2 é FAILNÃO PRONTO — resolver antes de prosseguir
T4 tem objetos remanescentesAuto-remediar (desmontagem), depois re-verificar

Esta seção define um fluxo de trabalho determinístico para assistentes de IA (Claude Code, Copilot, etc.) executando as etapas de automação da API. Seguir este protocolo elimina adivinhações — cada ponto de decisão tem um caminho de resolução definido.

Resolva cada variável nesta ordem exata. Pare na primeira fonte que fornecer um valor não-placeholder:

  1. Verificar arquivo .env — procure por .env na raiz do repositório. Se existir, analise todos os pares KEY=VALUE.
  2. Verificar ambiente shell — execute env | grep F5XC_ para encontrar quaisquer valores já exportados na sessão atual.
  3. Identificar valores ausentes — compare os valores resolvidos contra a tabela de obrigatórios/opcionais abaixo. Um valor está “ausente” se estiver inexistente, vazio ou ainda definido como um valor padrão placeholder (ex.: example-api-token, example-tenant, example-namespace, app.example.com, user@example.com).
  4. Solicitar ao operador humano — para cada variável obrigatória ausente, pergunte ao operador o valor. Não prossiga até que todas as variáveis obrigatórias estejam resolvidas.
  5. Aplicar padrões — para cada variável opcional ausente, use o padrão da tabela abaixo sem solicitar.

String vazia = ausente: Uma variável exportada com valor vazio (ex.: F5XC_HC_NAME="") é tratada da mesma forma que uma variável não definida — aplique o padrão. Use expansão de parâmetro shell (${F5XC_HC_NAME:-csd-hc}) para aplicar padrões em um único passo.

  1. Exibir confirmação — mostre a tabela final de variáveis resolvidas ao operador e aguarde aprovação antes de executar qualquer chamada de API.

Substituição no estágio de Preparação: Durante o Estágio 1 Preparação, pule a espera no passo 6. Exiba a tabela de variáveis resolvidas para registro, depois prossiga imediatamente. Os passos 1–5 ainda se aplicam — se alguma variável obrigatória estiver ausente após verificar .env e shell, pare e reporte as variáveis ausentes.

VariávelObrigatóriaPadrãoPlaceholder (tratar como ausente)
F5XC_API_TOKENSimexample-api-token
F5XC_API_URLSimhttps://example-tenant.console.ves.volterra.io
F5XC_NAMESPACESimexample-namespace
F5XC_DOMAINNAMESimapp.example.com
F5XC_ROOT_DOMAINSimexample.com
F5XC_LB_NAMESimexample-lb-name, example-lb
F5XC_EMAILSimuser@example.com
F5XC_HC_NAMEOpcionalcsd-hc
F5XC_ORIGIN_IPOpcional44.232.69.192
F5XC_ORIGIN_POOLOpcionalcsd-origin
F5XC_ORIGIN_PORTOpcional3000

O assistente de IA opera em um de três modos durante a demo:

ModoQuando ativoComportamento
NormalPadrão durante Preparação, Execução, DesmontagemApenas comandos documentados literalmente
DebugAuto-ativa em caso de falhaResolução criativa de problemas, atualizar documentação
Q&ADurante estágio de Q&AImprovisacional — comandos ad-hoc permitidos para responder perguntas da audiência

Modo normal (padrão):

  • Cada chamada de API, consulta de verificação e comando shell deve vir literalmente dos arquivos de fase (Fase 1–4) ou da seção de Verificação Pré-voo acima
  • Substitua apenas placeholders xTOKENx com valores de variáveis resolvidos
  • Não construa endpoints de API, filtros jq ou comandos cURL a partir de conhecimento geral ou inferência
  • Se um comando necessário não estiver documentado, pare e reporte ao operador: “Esta etapa de verificação não está coberta pela documentação da fase”

Modo debug (auto-ativa em caso de falha):

  • Ativa automaticamente quando um comando documentado produz um resultado inesperado: resposta HTTP não-2xx, erro de parse jq, timeout de comando ou corpo de resposta que contradiz a tabela de evidências
  • No modo debug, o assistente de IA pode construir comandos diagnósticos, inspecionar respostas brutas da API, testar variações de endpoint e usar resolução criativa de problemas para encontrar a causa raiz
  • Prefixe toda saída de debug com [DEBUG] para que o operador possa distinguir atividade diagnóstica da execução normal
  • Documente o que você aprender: após resolver um problema, atualize o arquivo de fase relevante ou seção de resolução de problemas com:
    1. O cenário de falha (o que deu errado)
    2. O que foi tentado e NÃO funcionou (para que execuções futuras não repitam)
    3. A resolução funcional (o comando ou correção que resolveu)
  • O objetivo do modo debug é eliminar a si mesmo — cada sessão de debug deve produzir uma atualização de documentação que torne a próxima execução totalmente determinística
  • Uma vez que a documentação esteja atualizada e o problema resolvido, retorne ao modo normal e retome a partir da última etapa documentada bem-sucedida
  • Se o modo debug não conseguir resolver o problema, reporte as descobertas ao operador e pare — não continue para a próxima fase

Modo Q&A (durante estágio de Q&A):

  • Ativo apenas durante o estágio de Q&A da reunião, após a conclusão da demo
  • O assistente de IA pode construir chamadas de API ad-hoc, executar comandos diagnósticos, navegar para páginas não roteirizadas e modificar o ambiente de demo ao vivo para ilustrar respostas às perguntas da audiência
  • Sem prefixo [DEBUG] — este é comportamento improvisacional intencional, não recuperação de erros
  • Usa a seção de Expertise em Produto CSD em DEMO_EXECUTOR.md como a base de conhecimento para perguntas sobre o produto

O acúmulo de initScript é uma fonte comum de falhas na demo. Cada chamada navigate_page com um parâmetro initScript adiciona o script a uma lista persistente que executa em cada carregamento de documento subsequente. Siga estas regras:

  • Sempre navegue para about:blank antes de qualquer navegação com initScript para limpar scripts acumulados de execuções anteriores
  • Use new_page com isolatedContext ao alternar entre fases da demo (ex.: Fase 2 → Fase 3) para garantir um estado de navegador completamente limpo
  • Recuperação de páginas não responsivas — se timeouts de take_screenshot ou take_snapshot ocorrerem, o contexto do navegador está com recursos esgotados. Use new_page com isolatedContext para criar um contexto novo, depois tente novamente a partir da etapa de navegação about:blank
  • Consulte os avisos da simulação de ataque da Fase 2 para orientação detalhada sobre falhas transitórias de origem e recuperação de esgotamento de recursos

Após cada chamada de API, o assistente de IA deve apresentar evidências estruturadas ao operador humano usando este formato:

Etapas de criação (POST):

CampoValorStatus
Status HTTP200PASS
Nome do Objetocsd-origin
Propriedade Chave(extraída via jq)

Após cada etapa de criação, execute um GET para confirmar que o objeto existe e exiba suas propriedades chave. Se o GET retornar 404, reporte FAIL e pare.

Etapas de verificação (GET/dig):

TesteResultadoStatus
DNS-1: Registro A198.51.100.10PASS
LB-1: Estado do HTTP LBVIRTUAL_HOST_READYPASS
LB-2: Estado do HTTPS LBVIRTUAL_HOST_READYINFO (opcional)
TLS-1: Estado do CertCertificateValidINFO (opcional)
CSD-1: Tag JSscriptTag presentePASS

Referencie os IDs de caso de teste de Diagnósticos e Verificação (DNS-1, TLS-1, LB-1, CSD-1, etc.) como o padrão de verificação para cada camada.

A execução de fases é sequencial e bloqueada: cada fase deve atingir PASS em todas as verificações obrigatórias antes que a próxima fase comece. A demo segue um ciclo de vida de reunião em quatro estágios — consulte a seção Estágios da Reunião em DEMO_EXECUTOR.md para frases-gatilho e regras comportamentais.

O assistente de IA segue esta sequência:

  1. Preparar — resolver variáveis, executar verificações pré-voo, confirmar ambiente limpo (pode ser executado separadamente antes da reunião)
  2. Introdução — o SE se apresenta e declara os objetivos de resultado (visibilidade de ameaças client-side, conformidade PCI, detecção em tempo real)
  3. Executar Fase 1 (Etapas 1–7) — criação e verificação de infraestrutura; todas as verificações da Fase 1 devem PASSAR antes de prosseguir
  4. Executar Fase 2 (Etapas 8–9) — simulação de ataque e verificação de detecção via API; assistentes de IA com automação de navegador executam as etapas do navegador diretamente, operadores sem ferramentas de navegador as realizam manualmente
  5. Executar Fase 3 — aplicar mitigação para todos os domínios detectados, re-executar ataque, verificar se o bloqueio é eficaz
  6. Conclusão — reafirmar objetivos de resultado, resumir evidências de cada fase, destacar detecções e mitigações principais
  7. Q&A — estágio improvisacional, demo permanece ao vivo, SE responde perguntas da audiência e faz perguntas de retorno
  8. Desmontagem (pós-reunião) — Fase 4, confirmação explícita do operador necessária, excluir todos os objetos na ordem inversa de dependência, confirmar ambiente limpo

Se alguma etapa retornar FAIL, pare e reporte a falha com o link da seção de resolução de problemas relevante antes de continuar.

  • Um token de API do F5 XC — gere um em AdministraçãoCredenciaisCredenciais de API
  • curl e jq instalados localmente
  • Um namespace com permissões para criar healthchecks, origin pools e HTTP load balancers

Crie um arquivo .env com os valores do seu ambiente. Um modelo é fornecido no repositório:

Terminal window
cp .env.example .env

Edite .env com seus valores reais:

.env
# Required — your environment
F5XC_API_TOKEN=example-api-token
F5XC_API_URL=https://example-tenant.console.ves.volterra.io
F5XC_DOMAINNAME=app.example.com
F5XC_EMAIL=user@example.com
F5XC_LB_NAME=example-lb-name
F5XC_NAMESPACE=example-namespace
F5XC_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

Carregue o arquivo para inserir as variáveis na sua sessão shell:

Terminal window
set -a && source .env && set +a

Cada placeholder xTOKENx nos comandos curl mapeia diretamente para uma variável de ambiente — por exemplo, xF5XC_API_TOKENx corresponde a $F5XC_API_TOKEN. Você pode substituir estes valores usando o formulário interativo no topo da página, ou deixar um assistente de IA como o Claude Code ler seu .env e construir os comandos para você.

TokenPadrãoDescrição
xF5XC_API_URLxhttps://example-tenant.console.ves.volterra.ioURL da API do Console XC
xF5XC_API_TOKENxexample-api-tokenToken de credencial da API
xF5XC_EMAILxuser@example.comEndereço de email para notificações CSD
xF5XC_NAMESPACExexample-namespaceNamespace
xF5XC_LB_NAMExexample-lbNome base do HTTP Load Balancer (cria ${name}-http e ${name}-https)
xF5XC_DOMAINNAMExapp.example.comFQDN a proteger
xF5XC_ROOT_DOMAINxexample.comDomínio raiz (eTLD+1) para domínio protegido CSD
xF5XC_ORIGIN_POOLxcsd-originNome do origin pool
xF5XC_ORIGIN_IPx44.232.69.192IP do servidor de origem
xF5XC_ORIGIN_PORTx3000Porta do servidor de origem
xF5XC_HC_NAMExcsd-hcNome do healthcheck

Esta seção resume o fluxo de trabalho completo do exercício para scripting ou automação.

  1. Clone o repositório e copie o modelo de ambiente: cp .env.example .env
  2. Edite .env com a URL do seu tenant, token de API, namespace e valores de domínio
  3. Carregue o ambiente: set -a && source .env && set +a
  4. Execute cada fase na ordem, verificando PASS em cada bloco de Evidências antes de prosseguir para a próxima fase

Os valores são resolvidos usando o protocolo determinístico definido no Protocolo de Execução para Assistente de IA:

  1. Arquivo .env — analise pares KEY=VALUE da raiz do repositório
  2. Ambiente shell — verifique env | grep F5XC_ para valores exportados
  3. Detecção de placeholder — sinalize qualquer valor que corresponda a um padrão placeholder (ex.: example-api-token, example-namespace) como ausente
  4. Solicitar operador — pergunte cada variável obrigatória ausente
  5. Aplicar padrões — use padrões incorporados para variáveis opcionais ausentes
  6. Confirmar — exiba a tabela de variáveis resolvidas e aguarde aprovação do operador
  1. Fase 1 — Construir: Implantar infraestrutura (healthcheck, origin pool, HTTP LB + HTTPS LB), configurar DNS, habilitar CSD, registrar domínio protegido, verificar todos os componentes. O HTTP LB é o alvo principal da demo; o HTTPS LB é opcional.
  2. Fase 2 — Atacar: Executar a simulação de ataque em um navegador usando URLs http://, aguardar 5–10 minutos, verificar detecções via API (/scripts, /detected_domains, /formFields)
  3. Fase 3 — Mitigar: Confirmar baseline limpo, executar ataque (prova antes), POST de cada domínio para /mitigated_domains, verificar mitigações aplicadas, re-executar ataque usando URLs http:// (prova depois), apresentar comparação antes/depois
  4. Fase 4 — Desmontar (requer confirmação humana explícita): Excluir HTTPS LB → HTTP LB → origin pool → limpeza da zona DNS (apenas registros manuais) → healthcheck → domínio protegido. Não exclua a zona DNS.
TokenDescriçãoPadrão
xF5XC_API_URLxURL da API do Console XChttps://example-tenant.console.ves.volterra.io
xF5XC_API_TOKENxToken de credencial da API(fornecido pelo usuário)
xF5XC_EMAILxEmail para notificação CSDuser@example.com
xF5XC_NAMESPACExNamespaceexample-namespace
xF5XC_LB_NAMExNome base do HTTP Load Balancer (cria ${name}-http e ${name}-https)example-lb
xF5XC_DOMAINNAMExFQDN a protegerapp.example.com
xF5XC_ROOT_DOMAINxDomínio raiz (eTLD+1)example.com
xF5XC_ORIGIN_POOLxNome do origin poolcsd-origin
xF5XC_ORIGIN_IPxIP do servidor de origem44.232.69.192
xF5XC_ORIGIN_PORTxPorta do servidor de origem3000
xF5XC_HC_NAMExNome do healthcheckcsd-hc

A especificação do HTTP Load Balancer usa grupos de escolha oneOf onde exatamente uma opção deve ser definida por grupo. Definir zero ou mais de uma opção em um grupo causa um erro 422.

Escolhas principais relacionadas ao CSD:

Grupo de EscolhaOpçõesPadrão CSD
client_side_defense_choiceclient_side_defense, disable_client_side_defenseclient_side_defense
java_script_choice (aninhado no CSD)disable_js_insert, js_insert_all_pages, js_insert_all_pages_except, js_insertion_rulesjs_insert_all_pages

Escolha de tipo de listener (HTTP vs HTTPS):

A demo cria dois LBs com configurações de listener diferentes:

LBEscolha de ListenerConfiguração
${F5XC_LB_NAME}-http (primário)http"http": { "dns_volterra_managed": true, "port": 80 }
${F5XC_LB_NAME}-https (secundário)https_auto_cert"https_auto_cert": { "http_redirect": true, "port": 443, ... }

As chaves http e https_auto_cert são mutuamente exclusivas — cada LB usa exatamente uma.

Escolhas aninhadas de auto-cert HTTPS (apenas LB secundário):

Grupo de EscolhaOpçõesPadrão
portport (número)443
server_header_choicedefault_header, server_name, append_server_namedefault_header
path_normalize_choiceenable_path_normalize, disable_path_normalizeenable_path_normalize
mtls_choiceno_mtls, use_mtlsno_mtls
default_loadbalancer_choicedefault_loadbalancer, non_default_loadbalancerdefault_loadbalancer

Escolhas aninhadas de single_lb_app (configuração ML):

O objeto single_lb_app tem seus próprios grupos oneOf obrigatórios. Definir single_lb_app: \{\} sem essas escolhas aninhadas causa um erro 400.

Grupo de EscolhaOpçõesPadrão
api_discovery_choicedisable_discovery, enable_discoverydisable_discovery
ddos_detection_choicedisable_ddos_detection, enable_ddos_detectiondisable_ddos_detection
malicious_user_detection_choicedisable_malicious_user_detection, enable_malicious_user_detectiondisable_malicious_user_detection

Outras escolhas em nível de LB (todas definidas como desabilitar/padrão):

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 — token de API é inválido ou expirou. Regenere em AdministraçãoCredenciais.
  • 403 Forbidden — token não tem permissões para o namespace. Verifique os role bindings.
  • 404 Not Found — namespace ou nome do objeto está incorreto. Liste objetos com GET /api/config/namespaces/\{namespace\}/\{object_type\}.
  • 409 Conflict — objeto já existe. Para domínios protegidos, um 409 com “domain already exists (in uriList)” significa que o domínio raiz já está registrado no tenant — esta é uma condição de sucesso, não um erro. Para outros objetos, exclua primeiro ou use PUT para atualizar.
  • 422 Unprocessable Entity — validação de schema JSON falhou. Causas comuns: escolha oneOf ausente, múltiplas escolhas definidas no mesmo grupo, tipo de campo errado. Verifique a mensagem de erro para o campo específico.
  • Limite de objetos esgotado (código de erro 8, mensagem "Object kind {kind} has exhausted limits({N})") — tenant atingiu um limite de cota de objetos. A API retorna HTTP 200 com um corpo JSON de erro contendo "code": 8, não HTTP 429. Você pode verificar proativamente a capacidade de cota antes de encontrar este erro usando a API de Uso de Cotas (GET /api/web/namespaces/system/quota/usage?namespace=system). O comportamento depende do tipo de objeto:
    • Healthcheck (limite ~150): Não bloqueante — pule a Fase 1 Etapa 1 e crie o origin pool sem referência de healthcheck. CSD não depende de monitoramento de saúde.
    • Endpoint (limite ~500): Bloqueante — endpoints são sub-objetos criados dentro de origin pools. Se este limite for atingido, a criação do origin pool falha. Exclua origin pools não utilizados (o que libera seus sub-objetos endpoint) ou entre em contato com seu administrador para aumentar o limite do tenant.
    • Origin pool: Bloqueante — exclua origin pools não utilizados ou entre em contato com seu administrador.
    • HTTP load balancer: Bloqueante — exclua load balancers não utilizados ou entre em contato com seu administrador.
    • Domínio protegido: Bloqueante — exclua domínios protegidos não utilizados ou entre em contato com seu administrador.

Se a Fase 1 Etapa 2 (Verificar Healthcheck está Vinculado) mostrar um array vazio []:

  1. Exclua o origin pool: DELETE /api/config/namespaces/{namespace}/origin_pools/{pool_name}
  2. Verifique se o healthcheck existe: GET /api/config/namespaces/{namespace}/healthchecks/{hc_name}
  3. Recrie o origin pool com a referência correta de healthcheck

Se o state do load balancer permanecer VIRTUAL_HOST_PENDING_A_RECORD após a Fase 1 Etapa 4:

  1. Verifique se a zona DNS existe (apenas DNS gerenciado pelo F5 XC):

    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'

    Um 404 significa que a zona DNS não foi criada no F5 XC.

  2. Verifique allow_http_lb_managed_records — se a zona existe mas o LB está pendente, registros gerenciados podem estar desabilitados:

    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'

    Se false ou null, habilite usando o comando PUT na Fase 1 Etapa 4, Opção A.

  3. DNS Externo — se o F5 XC não é autoritativo, crie os registros A e CNAME ACME manualmente no seu provedor DNS (consulte Fase 1 Etapa 4, Opção B).

  4. Verifique a resolução — após corrigir o DNS, confirme:

    Terminal window
    dig +short xF5XC_DOMAINNAMEx A

    Se o registro A resolver, o LB transiciona para VIRTUAL_HOST_READY. Consulte a cada 30 segundos, até 4 iterações (2 minutos no total). Se ainda estiver VIRTUAL_HOST_PENDING_A_RECORD após 2 minutos, re-verifique a propagação DNS e reporte ao operador.

O CSD deve ser habilitado no nível do tenant. Entre em contato com seu administrador F5 XC para habilitar o Client-Side Defense para seu tenant. Esta é uma configuração em nível de tenant que não pode ser configurada via API.

  1. Verifique se o CSD está habilitado no nível do tenant (Fase 1 Etapa 5)
  2. Confirme que o load balancer tem client_side_defense definido na especificação
  3. Verifique se o endpoint de configuração JS retorna um scriptTag
  4. Visite o domínio protegido em um navegador e visualize o código fonte da página para confirmar que o script está injetado

Um 409 com “domain already exists (in uriList)” significa que o domínio raiz já está registrado no tenant. Domínios protegidos são com escopo de tenant — não pertencem a nenhum namespace individual. Esta é uma condição de sucesso: o domínio já está protegido e nenhuma ação adicional é necessária. Continue para a Fase 1 Etapa 7.

Se o cert_state do HTTPS LB mostrar AutoCertDomainRateLimited, isso significa que o Let’s Encrypt limitou a emissão de certificados para este domínio. Isso é esperado em ambientes de demo onde a infraestrutura é frequentemente criada e destruída.

Impacto: O HTTPS LB não servirá tráfego até que o limite de taxa seja resetado (tipicamente 1 hora). O HTTP LB não é afetado de forma alguma — todo o tráfego da demo prossegue normalmente via http://.

Resolução: Nenhuma ação necessária para progressão da demo. O HTTP LB (${F5XC_LB_NAME}-http) é o alvo principal da demo e não depende de provisionamento de certificado. Se HTTPS for desejado, aguarde o reset do limite de taxa e o certificado será auto-provisionado.

Certificado Preso — Recriação Limpa (Opcional)

Seção intitulada “Certificado Preso — Recriação Limpa (Opcional)”

Quando o certificado do HTTPS LB está preso em DomainChallengePending ou PreDomainChallengePending por mais de 15 minutos, o caminho de recuperação mais rápido é excluir o HTTPS LB e recriá-lo. Com a zona DNS já configurada (registros gerenciados habilitados), a recriação limpa tipicamente produz CertificateValid em 5–7 minutos — a menos que esteja com limite de taxa.

  1. Exclua o HTTPS Load Balancer: DELETE .../http_loadbalancers/${F5XC_LB_NAME}-https
  2. Aguarde 30 segundos para limpeza da plataforma
  3. Recrie o HTTPS Load Balancer (Fase 1 Etapa 3)
  4. Monitore o estado do certificado (Fase 1 Etapa 7) — espere CertificateValid dentro de 5–10 minutos

A especificação canônica da API do F5 Distributed Cloud está disponível em:

https://docs.cloud.f5.com/docs-v2/downloads/f5-distributed-cloud-open-api.zip

Este ZIP contém especificações OpenAPI 3.0 para todos os grupos de API incluindo ves.io.schema.views.http_loadbalancer, ves.io.schema.healthcheck, ves.io.schema.origin_pool e ves.io.schema.shape.csd. Use estas especificações para validar payloads JSON e descobrir campos adicionais.