Ir al contenido

Demo

Esta guía le orienta a través de un ejercicio completo de Defensa del lado del cliente en F5 Distributed Cloud utilizando la API — estructurado en cuatro fases que un asistente de IA u operador humano puede ejecutar de principio a fin. Cada paso incluye un comando curl listo para ejecutar con valores de marcador de posición que puede personalizar mediante el formulario en la parte superior de la página, un archivo .env, o cualquier herramienta de Automatización.

FaseObjetivoPasos
Fase 1 — ConstrucciónDesplegar y validar la infraestructura CSD completaPasos 1–7
Fase 2 — AtaqueGenerar tráfico de ataque simulado y confirmar que CSD lo detectóPasos 8–9
Fase 3 — MitigaciónPrueba antes/después de mitigación — ejecutar ataque, aplicar mitigaciones, volver a ejecutar ataque, compararPasos 1–6
Fase 4 — DesmontajeEliminar todos los objetos de despliegue tras confirmación explícitaDesmontaje

Antes de iniciar la Fase 1, verifique que el entorno esté limpio. Ejecute estas verificaciones de API para determinar si existen objetos residuales de una ejecución anterior:

Ventana de terminal
# 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
)
}'

Cuando todos los objetos de la Fase 1 existen (200) y planea omitir hasta la Fase 2, ejecute los comandos de verificación del Paso 7 de la Fase 1 para confirmar el estado de la infraestructura antes de omitir. Use los comandos exactos de Fase 1 — Paso 7: Verificar:

  1. Resolución DNS: dig +short xF5XC_DOMAINNAMEx A
  2. Estado del LB HTTP: GET .../http_loadbalancers/xF5XC_LB_NAMEx-http canalizando a jq '{state: .spec.state}' — debe mostrar VIRTUAL_HOST_READY
  3. Configuración JS de CSD: GET .../js_configuration — debe contener scriptTag
  4. Estado de CSD: GET .../status canalizando a jq '{configured: .isConfigured, enabled: .isEnabled}' — ambos deben ser true

Todas las verificaciones requeridas (DNS-1, LB-1, CSD-1, CSD-2) deben pasar (PASS) antes de omitir hacia la Fase 2. Si alguna verificación falla, ejecute la Fase 1 comenzando desde el paso fallido.

La verificación previa al vuelo anterior verifica que el entorno esté limpio. La matriz de preparación a continuación verifica que el entorno sea capaz — que todos los requisitos previos, cuotas, conectividad y servicios de plataforma estén en su lugar para una demostración exitosa. Ejecute esta matriz antes de cada reunión como parte de la etapa de Preparación.

Cada verificación tiene un ID de prueba, un nivel (T0–T5), un criterio PASS/FAIL/WARN y una ruta de corrección. Los niveles son secuenciales — un FAIL en un nivel anterior bloquea los niveles posteriores para que no se ejecuten.

NivelCategoría¿Bloquea la demostración?Propósito
T0Conectividad y distribución y Autenticación¿Podemos llegar a la plataforma y autenticarnos?
T1Cuotas y Capacidad (si está en el límite)¿Hay espacio para crear objetos de demostración?
T2Requisitos previos de la Plataforma¿Están configurados los servicios a nivel de tenant?
T3Estado del Servidor de origenAdvertencia¿Está respondiendo la aplicación backend?
T4Entorno limpioAuto-corrección¿Existen objetos residuales de una ejecución anterior?
T5Preparación de certificadosInformativo¿Funcionará HTTPS o debemos planificar solo HTTP?

Estas verificaciones confirman que el host de ejecución puede llegar a la API de F5 XC y que las credenciales son válidas.

Ventana de terminal
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
)
}'
Ventana de terminal
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
)
}'
Ventana de terminal
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
)
}'

Las sondas no destructivas prueban los permisos de lectura y escritura para cada tipo de objeto que necesita la demostración. La lectura se prueba mediante GET en los endpoints de lista. La escritura se prueba mediante DELETE en objetos conocidos inexistentes — la API devuelve 403 si RBAC deniega la operación, o 404 si la operación está permitida pero el objeto no existe. Esta técnica de efecto cero evita crear objetos de sonda temporales.

Ventana de terminal
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 verificaciones consultan la API de Uso de Cuota del tenant para determinar límites, uso actual y capacidad restante para cada tipo de objeto que necesita la demostración. Esto reemplaza las pruebas de sonda y eliminación con una única llamada a la API de solo lectura que reporta números exactos.

Consulte el endpoint de uso de cuota a nivel de tenant y calcule un estado determinista PASS/WARN/FAIL para cada tipo de objeto que necesita la demostración. Este endpoint requiere el namespace system. Una sola llamada a la API verifica todas las cuotas a nivel de plataforma a la vez.

La verificación define un array demo_needs que especifica cuántos objetos de cada tipo consumirá la demostración, si el tipo es requerido y el mínimo necesario para que la demostración continúe. El filtro jq compara remaining contra needed y calcula el campo status de forma determinista — no se requiere interpretación del operador.

Ventana de terminal
# 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

Salida de la verificación — el campo gate es el veredicto determinista único:

  • PASS — todos los tipos de objetos tienen remaining >= needed. La demostración puede continuar.
  • WARN — al menos un tipo tiene capacidad reducida pero se cumple el mínimo para continuar (p. ej., solo hay 1 ranura de LB disponible en lugar de 2, o la cuota de healthcheck está agotada). La demostración puede continuar con limitaciones.
  • FAIL — al menos un tipo requerido tiene remaining < min_proceed. La demostración no puede continuar hasta que se libere cuota.

Ejemplo de salida (WARN — endpoint al límite de capacidad, healthcheck casi lleno):

{
"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 gateAcción
PASSContinuar a PF-T1-4 (verificación de dominio protegido), luego T2
WARNAnotar limitaciones en el informe de preparación, continuar con capacidad reducida
FAILDetener — reportar qué tipos están agotados y los pasos de corrección a continuación

Corrección por tipo:

TipoCorrección
healthcheckEliminar healthchecks no utilizados para liberar capacidad. La demostración continúa sin healthcheck (CSD no requiere uno).
origin_poolEliminar origin pools no utilizados o contactar a su administrador para aumentar el límite del tenant.
endpointEliminar origin pools no utilizados en otros namespaces para liberar capacidad de endpoints (los endpoints son sub-objetos de los origin pools), o contactar a su administrador.
http_loadbalancerEliminar load balancers no utilizados o contactar a su administrador. Si solo hay 1 ranura disponible, se creará el LB HTTP (primario) pero se omitirá el LB HTTPS (secundario).

Los dominios protegidos de CSD no aparecen en la API de Uso de Cuota de la plataforma. Use una verificación basada en sondas: cree y elimine inmediatamente un dominio protegido de sonda.

Ventana de terminal
# 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: Verificaciones de cuota basadas en sondas

Sección titulada «Alternativa: Verificaciones de cuota basadas en sondas»

Si PF-T1-0 falla (la API de Uso de Cuota devuelve 403, 404 o un formato inesperado), recurra a las verificaciones de sonda y eliminación para las cuotas de healthcheck, origin pool, endpoint y load balancer. Estas verificaciones crean un objeto temporal y lo eliminan inmediatamente — si la creación devuelve el código de error 8 con “exhausted limits”, la cuota está llena.

Cada sonda alternativa usa el mismo patrón: crear un objeto temporal, calcular un estado determinista a partir de la respuesta y luego eliminar la sonda. El campo status es PASS si el objeto fue creado (.metadata.name presente), WARN o FAIL si el código de error es 8 (límites agotados), según si el tipo es requerido.

Sonda de healthcheck (WARN si está agotado — los healthchecks son opcionales para CSD):

Ventana de terminal
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

Sonda de origin pool y endpoint (FAIL si está agotado — ambos son requeridos):

Ventana de terminal
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

Sonda de HTTP load balancer (FAIL si está agotado — los LBs son requeridos):

Ventana de terminal
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 verificaciones validan los servicios a nivel de tenant de los que depende la demostración.

Ventana de terminal
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
)
}'
Ventana de terminal
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: Registros DNS gestionados habilitados

Sección titulada «PF-T2-3: Registros DNS gestionados habilitados»

Solo ejecute si PF-T2-2 devolvió 200 (existe la zona DNS de F5 XC).

Verificar estado actual:

Ventana de terminal
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-corrección si es necesario: Si el status es WARN Y PF-T2-4 muestra servidores de nombres de F5 XC (ns1.f5clouddns.com, ns2.f5clouddns.com), habilite automáticamente los registros gestionados mediante GET+PUT:

Ventana de terminal
# 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 .

Luego vuelva a verificar para confirmar que la actualización tuvo efecto:

Ventana de terminal
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'
ResultadoAutoridad DNSEstadoCorrección
trueCualquieraPASSLos registros DNS gestionados por LB se crearán automáticamente
false/nullF5 XCAuto-correcciónHabilitar mediante GET+PUT, verificar, reportar resultado
false/nullExternoINFOLos registros gestionados no aplican para DNS externo — La Fase 1 Paso 4 usará la Opción B (creación manual de registros)
Auto-corrección fallidaF5 XCFAILEl token puede carecer de acceso de escritura al namespace del sistema — contacte al administrador del tenant

PF-T2-4: Autoridad del servidor de nombres DNS

Sección titulada «PF-T2-4: Autoridad del servidor de nombres DNS»
Ventana de terminal
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 verificaciones validan que la aplicación backend es accesible.

Verificación de condición de omisión: Calcule si la IP de origen es una dirección TEST-NET del RFC 5737 antes de ejecutar las pruebas de conectividad:

Ventana de terminal
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)
}'

Si status es SKIP, registre PF-T3-1 y PF-T3-2 como SKIP y continúe con T4. De lo contrario, ejecute las verificaciones a continuación.

PF-T3-1: Conectividad del Servidor de origen

Sección titulada «PF-T3-1: Conectividad del Servidor de origen»
Ventana de terminal
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
)
}'

Solo ejecute si PF-T3-1 devolvió un estado HTTP válido:

Ventana de terminal
curl -s --max-time 10 "http://xF5XC_ORIGIN_IPx:xF5XC_ORIGIN_PORTx/" \
| grep -qi '</html>' && echo "PASS: HTML content" || echo "WARN: No HTML detected"
ResultadoEstadoCorrección
PASS: HTML contentPASSEl origen sirve páginas HTML (requerido para la inyección JS de CSD)
WARN: No HTML detectedWARNEl origen puede ser un servicio solo de API o devolver contenido no HTML — la inyección JS de CSD requiere respuestas de páginas HTML

Estas verificaciones validan que no quedan objetos de configuración de F5 XC nombrados de una ejecución de demostración anterior — HTTP load balancers, HTTPS load balancers, origin pools, healthchecks, dominios protegidos y dominios mitigados. T4 trata sobre la limpieza a nivel de objetos: si existen objetos de API que entrarían en conflicto con la creación en la Fase 1. No prueba direcciones IP, conectividad de red ni el estado del origen (esas preocupaciones pertenecen a T3).

Ejecute los seis comandos de verificación previa al vuelo de la sección Verificación previa al vuelo anterior. Aplique la Lógica de decisión para determinar los próximos pasos. Si existen objetos, el desmontaje automático se realiza durante la etapa de Preparación (no se necesita confirmación).

También verifique y elimine los objetos de sonda obsoletos de ejecuciones de verificación previa al vuelo interrumpidas anteriormente. Estas sondas solo se crean cuando la API de Uso de Cuota no está disponible y se usaron las verificaciones alternativas basadas en sondas, o para la sonda de dominio protegido (PF-T1-4) que siempre usa verificación basada en sondas:

Ventana de terminal
# 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 devuelve 200 (vacío {}) si el objeto existía, o 404 si no existía — ambos son esperados.

Estas verificaciones evalúan si HTTPS funcionará o si la demostración debe planificarse solo para HTTP.

PF-T5-1: Historial reciente de emisión de certificados

Sección titulada «PF-T5-1: Historial reciente de emisión de certificados»

Verifique si recientemente se emitió un certificado de Let’s Encrypt para el dominio de demostración. Los ciclos frecuentes de creación y destrucción pueden agotar el límite de frecuencia semanal (5 certificados duplicados por semana por dominio).

PF-T5-2: Estado del certificado del LB HTTPS existente

Sección titulada «PF-T5-2: Estado del certificado del LB HTTPS existente»

Solo ejecute si existe un LB HTTPS de una ejecución anterior (PF-T4 encontró objetos):

Ventana de terminal
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

Después de ejecutar todos los niveles, presente un informe de preparación consolidado:

## Preparación para la demostración: LISTO / NO LISTO / LISTO CON ADVERTENCIAS
### T0: Conectividad y Autenticación
| Verificación | Resultado | Estado |
|---|---|---|
| PF-T0-1: Conectividad de API | 200 | PASS |
| PF-T0-2: Acceso al namespace | 200 | PASS |
| PF-T0-3: Acceso a la API de CSD | 200 | PASS |
### T1: Cuotas y Capacidad
| Verificación | Tipo | Límite | Uso | Restante | Necesario | Estado |
|---|---|---|---|---|---|---|
| PF-T1-0: Verificación de uso de cuota | `healthcheck` | 150 | 148 | 2 | 1 | PASS |
| PF-T1-0: Verificación de uso de cuota | `origin_pool` | unlimited | 420 | unlimited | 1 | PASS |
| PF-T1-0: Verificación de uso de cuota | `endpoint` | 500 | 498 | 2 | 1 | PASS |
| PF-T1-0: Verificación de uso de cuota | `http_loadbalancer` | unlimited | 116 | unlimited | 2 | PASS |
| PF-T1-0: Verificación de uso de cuota | **gate** | — | — | — | — | **PASS** |
| PF-T1-4: Dominio protegido | — | — | — | — | 1 | PASS (sonda) |
### T2: Requisitos previos de la Plataforma
| Verificación | Resultado | Estado |
|---|---|---|
| PF-T2-1: Estado de CSD en el tenant | configurado + habilitado | PASS |
| PF-T2-2: Zona DNS existente | 200 | PASS |
| PF-T2-3: Registros DNS gestionados | true | PASS |
| PF-T2-4: Autoridad del servidor de nombres DNS | f5clouddns.com | PASS |
### T3: Estado del Servidor de origen
| Verificación | Resultado | Estado |
|---|---|---|
| PF-T3-1: Conectividad del origen | Dirección TEST-NET (192.0.2.1) | SKIP |
| PF-T3-2: Contenido HTML | Dirección TEST-NET | SKIP |
### T4: Entorno limpio
| Verificación | Resultado | Estado |
|---|---|---|
| HTTP LB | 404 | PASS |
| HTTPS LB | 404 | PASS |
| Origin Pool | 404 | PASS |
| Healthcheck | 404 | PASS |
| Dominios protegidos | 0 | PASS |
| Dominios mitigados | 0 | PASS |
### T5: Preparación de certificados
| Verificación | Resultado | Estado |
|---|---|---|
| PF-T5-2: Estado del certificado | SKIP (sin LB HTTPS) | INFO |
### Advertencias
- (enumerar cualquier elemento WARN o INFO con contexto)

Reglas de estado general:

CondiciónEstado
Todas las verificaciones T0–T4 pasan (PASS)LISTO
Todas las verificaciones T0–T4 pasan (PASS) pero T3 o T5 tienen WARN/INFOLISTO CON ADVERTENCIAS
Cualquier verificación de T0, T1 o T2 es FAILNO LISTO — resolver antes de continuar
T4 tiene objetos residualesAuto-corrección (desmontaje), luego volver a verificar

Esta sección define un flujo de trabajo determinista para los asistentes de IA (Claude Code, Copilot, etc.) que ejecutan los pasos de Automatización de la API. Seguir este protocolo elimina las suposiciones — cada punto de decisión tiene una ruta de resolución definida.

Resuelva cada variable en este orden exacto. Deténgase en la primera fuente que proporcione un valor que no sea un marcador de posición:

  1. Verificar el archivo .env — busque .env en la raíz del repositorio. Si existe, analice todos los pares KEY=VALUE.
  2. Verificar el entorno de shell — ejecute env | grep F5XC_ para encontrar cualquier valor ya exportado en la sesión actual.
  3. Identificar valores faltantes — compare los valores resueltos con la tabla de requeridos/opcionales a continuación. Un valor está “faltante” si está ausente, vacío o todavía configurado como un valor predeterminado de marcador de posición (p. ej., example-api-token, example-tenant, example-namespace, app.example.com, user@example.com).
  4. Solicitar al operador humano — para cada variable requerida faltante, solicite al operador que proporcione el valor. No continúe hasta que todas las variables requeridas estén resueltas.
  5. Aplicar valores predeterminados — para cada variable opcional faltante, use el valor predeterminado de la tabla a continuación sin solicitar confirmación.

Cadena vacía = faltante: Una variable exportada con un valor vacío (p. ej., F5XC_HC_NAME="") se trata igual que una variable no establecida — aplique el valor predeterminado. Use la expansión de parámetros de shell (${F5XC_HC_NAME:-csd-hc}) para aplicar los valores predeterminados en un solo paso.

  1. Mostrar confirmación — muestre la tabla final de variables resueltas al operador y espere aprobación antes de ejecutar cualquier llamada a la API.

Anulación de la etapa de Preparación: Durante la Etapa 1 de Preparación, omita la espera en el paso 6. Muestre la tabla de variables resueltas para el registro y continúe de inmediato. Los pasos 1–5 aún se aplican — si alguna variable requerida falta después de verificar .env y el shell, deténgase y reporte las variables faltantes.

VariableRequeridaPredeterminadoMarcador de posición (tratar como faltante)
F5XC_API_TOKENexample-api-token
F5XC_API_URLhttps://example-tenant.console.ves.volterra.io
F5XC_NAMESPACEexample-namespace
F5XC_DOMAINNAMEapp.example.com
F5XC_ROOT_DOMAINexample.com
F5XC_LB_NAMEexample-lb-name, example-lb
F5XC_EMAILuser@example.com
F5XC_HC_NAMEOpcionalcsd-hc
F5XC_ORIGIN_IPOpcional44.232.69.192
F5XC_ORIGIN_POOLOpcionalcsd-origin
F5XC_ORIGIN_PORTOpcional3000

El asistente de IA opera en uno de tres modos durante la demostración:

ModoCuándo está activoComportamiento
NormalPredeterminado durante Preparación, Ejecución, DesmontajeÚnicamente comandos documentados literales
DepuraciónSe activa automáticamente al fallarResolución de problemas creativa, actualizar documentación
Preguntas y respuestasDurante la etapa de Preguntas y respuestasImprovisado — se permiten comandos ad-hoc para responder preguntas de la audiencia

Modo normal (predeterminado):

  • Cada llamada a la API, consulta de verificación y comando de shell debe provenir literalmente de los archivos de fase (Fases 1–4) o de la sección de Verificación previa al vuelo anterior
  • Sustituya únicamente los marcadores de posición xTOKENx con los valores de variables resueltos
  • No construya endpoints de API, filtros jq ni comandos cURL desde conocimiento general o inferencia
  • Si un comando necesario no está documentado, deténgase y reporte al operador: “Este paso de verificación no está cubierto por la documentación de fases”

Modo de depuración (se activa automáticamente al fallar):

  • Se activa automáticamente cuando un comando documentado produce un resultado inesperado: respuesta HTTP no 2xx, error de análisis de jq, tiempo de espera agotado del comando o cuerpo de respuesta que contradice la tabla de evidencias
  • En modo de depuración, el asistente de IA puede construir comandos de diagnóstico, inspeccionar respuestas de API sin procesar, probar variaciones de endpoints y usar resolución de problemas creativa para encontrar la causa raíz
  • Prefije toda la salida de depuración con [DEBUG] para que el operador pueda distinguir la actividad de diagnóstico de la ejecución normal
  • Documente lo que aprende: después de resolver un problema, actualice el archivo de fase relevante o la sección de resolución de problemas con:
    1. El escenario de fallo (qué salió mal)
    2. Qué se intentó y NO funcionó (para que ejecuciones futuras no lo repitan)
    3. La resolución que funcionó (el comando o corrección que lo solucionó)
  • El objetivo del modo de depuración es eliminarse a sí mismo — cada sesión de depuración debe producir una actualización de documentación que haga que la siguiente ejecución sea completamente determinista
  • Una vez que la documentación esté actualizada y el problema resuelto, regrese al modo normal y reanude desde el último paso documentado exitoso
  • Si el modo de depuración no puede resolver el problema, reporte los hallazgos al operador y deténgase — no continúe a la siguiente fase

Modo de Preguntas y respuestas (durante la etapa de Preguntas y respuestas):

  • Activo únicamente durante la etapa de reunión de Preguntas y respuestas, después de la conclusión de la demostración
  • El asistente de IA puede construir llamadas a la API ad-hoc, ejecutar comandos de diagnóstico, navegar a páginas no programadas y modificar el entorno de demostración en vivo para ilustrar respuestas a las preguntas de la audiencia
  • Sin prefijo [DEBUG] — este es comportamiento improvisado intencional, no recuperación de errores
  • Utiliza la sección de Experiencia en el producto CSD en DEMO_EXECUTOR.md como base de conocimiento para las preguntas sobre el producto

La acumulación de initScript es una fuente común de fallos en la demostración. Cada llamada a navigate_page con un parámetro initScript agrega el script a una lista persistente que se ejecuta en cada carga de documento posterior. Siga estas reglas:

  • Siempre navegue a about:blank antes de cualquier navegación con initScript para borrar los scripts acumulados de ejecuciones anteriores
  • Use new_page con isolatedContext al cambiar entre fases de demostración (p. ej., Fase 2 → Fase 3) para garantizar un estado de navegador completamente limpio
  • Recuperación de páginas que no responden — si take_screenshot o take_snapshot generan tiempos de espera agotados, el contexto del navegador está agotado de recursos. Use new_page con isolatedContext para crear un contexto nuevo y luego reintente desde el paso de navegación a about:blank
  • Consulte los avisos de la simulación de ataque de la Fase 2 para orientación detallada sobre fallos transitorios de origen y recuperación por agotamiento de recursos

Después de cada llamada a la API, el asistente de IA debe presentar evidencias estructuradas al operador humano usando este formato:

Pasos de creación (POST):

CampoValorEstado
Estado HTTP200PASS
Nombre del objetocsd-origin
Propiedad clave(extraída mediante jq)

Después de cada paso de creación, ejecute un GET para confirmar que el objeto existe y mostrar sus propiedades clave. Si el GET devuelve 404, reporte FAIL y deténgase.

Pasos de verificación (GET/dig):

PruebaResultadoEstado
DNS-1: Registro A198.51.100.10PASS
LB-1: Estado del LB HTTPVIRTUAL_HOST_READYPASS
LB-2: Estado del LB HTTPSVIRTUAL_HOST_READYINFO (opcional)
TLS-1: Estado del certificadoCertificateValidINFO (opcional)
CSD-1: Etiqueta JSscriptTag presentePASS

Consulte los IDs de casos de prueba de Diagnósticos y Verificación (DNS-1, TLS-1, LB-1, CSD-1, etc.) como estándar de verificación para cada capa.

La ejecución de fases es secuencial y con control de acceso: cada fase debe alcanzar PASS en todas las verificaciones requeridas antes de que comience la siguiente fase. La demostración sigue un ciclo de vida de reunión de cuatro etapas — consulte la sección de Etapas de reunión en DEMO_EXECUTOR.md para las frases desencadenantes y las reglas de comportamiento.

El asistente de IA sigue esta secuencia:

  1. Preparar — resolver variables, ejecutar verificaciones previas al vuelo, confirmar entorno limpio (puede ejecutarse por separado antes de la reunión)
  2. Introducción — el SE se presenta y establece los objetivos del resultado (visibilidad de amenazas del lado del cliente, cumplimiento PCI, detección en tiempo real)
  3. Ejecutar Fase 1 (Pasos 1–7) — creación y verificación de infraestructura; todas las verificaciones de la Fase 1 deben pasar (PASS) antes de continuar
  4. Ejecutar Fase 2 (Pasos 8–9) — simulación de ataque y verificación de detección mediante API; los asistentes de IA con Automatización de navegador ejecutan los pasos del navegador directamente, los operadores sin herramientas de navegador los realizan manualmente
  5. Ejecutar Fase 3 — aplicar mitigación para todos los dominios detectados, volver a ejecutar el ataque, verificar que el bloqueo es efectivo
  6. Conclusión — reafirmar los objetivos del resultado, resumir la evidencia de cada fase, destacar las detecciones y mitigaciones clave
  7. Preguntas y respuestas — etapa improvisada, la demostración permanece en vivo, el SE responde las preguntas de la audiencia y hace preguntas de retorno
  8. Desmontaje (posterior a la reunión) — Fase 4, se requiere confirmación explícita del operador, eliminar todos los objetos en orden de dependencia inversa, confirmar entorno limpio

Si algún paso devuelve FAIL, deténgase y reporte el fallo con el enlace a la sección de resolución de problemas relevante antes de continuar.

  • Un token de API de F5 XC — genere uno en AdministraciónCredencialesCredenciales de API
  • curl y jq instalados localmente
  • Un namespace con permisos para crear healthchecks, origin pools y HTTP load balancers

Cree un archivo .env con los valores de su entorno. Se proporciona una plantilla en el repositorio:

Ventana de terminal
cp .env.example .env

Edite .env con sus valores reales:

.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

Cargue el archivo para importar las variables en su sesión de shell:

Ventana de terminal
set -a && source .env && set +a

Cada marcador de posición xTOKENx en los comandos curl se corresponde directamente con una variable de entorno — por ejemplo, xF5XC_API_TOKENx corresponde a $F5XC_API_TOKEN. Puede sustituir estos valores usando el formulario interactivo en la parte superior de la página, o dejar que un asistente de IA como Claude Code lea su .env y construya los comandos por usted.

TokenPredeterminadoDescripción
xF5XC_API_URLxhttps://example-tenant.console.ves.volterra.ioURL de la API de la F5 XC Consola
xF5XC_API_TOKENxexample-api-tokenToken de credencial de API
xF5XC_EMAILxuser@example.comDirección de correo electrónico para notificaciones de CSD
xF5XC_NAMESPACExexample-namespaceNamespace
xF5XC_LB_NAMExexample-lbNombre base del HTTP Load Balancer (crea ${name}-http y ${name}-https)
xF5XC_DOMAINNAMExapp.example.comFQDN a proteger
xF5XC_ROOT_DOMAINxexample.comDominio raíz (eTLD+1) para el dominio protegido de CSD
xF5XC_ORIGIN_POOLxcsd-originNombre del origin pool
xF5XC_ORIGIN_IPx44.232.69.192IP del Servidor de origen
xF5XC_ORIGIN_PORTx3000Puerto del Servidor de origen
xF5XC_HC_NAMExcsd-hcNombre del healthcheck

Esta sección resume el flujo de trabajo completo del ejercicio para scripting o Automatización.

  1. Clone el repositorio y copie la plantilla de entorno: cp .env.example .env
  2. Edite .env con su URL de tenant, token de API, namespace y valores de dominio
  3. Cargue el entorno: set -a && source .env && set +a
  4. Ejecute cada fase en orden, verificando PASS en cada bloque de Evidencia antes de continuar a la siguiente fase

Los valores se resuelven utilizando el protocolo determinista definido en Protocolo de ejecución del asistente de IA:

  1. Archivo .env — analizar los pares KEY=VALUE desde la raíz del repositorio
  2. Entorno de shell — verificar env | grep F5XC_ para valores exportados
  3. Detección de marcadores de posición — marcar cualquier valor que coincida con un marcador de posición predeterminado (p. ej., example-api-token, example-namespace) como faltante
  4. Solicitar al operador — pedir cada variable requerida faltante
  5. Aplicar valores predeterminados — usar valores predeterminados integrados para variables opcionales faltantes
  6. Confirmar — mostrar la tabla de variables resueltas y esperar aprobación del operador
  1. Fase 1 — Construcción: Desplegar infraestructura (healthcheck, origin pool, LB HTTP + LB HTTPS), configurar DNS, habilitar CSD, registrar dominio protegido, verificar todos los componentes. El LB HTTP es el objetivo principal de la demostración; el LB HTTPS es opcional.
  2. Fase 2 — Ataque: Ejecutar la simulación de ataque en un navegador usando URLs http://, esperar 5–10 minutos, verificar detecciones mediante API (/scripts, /detected_domains, /formFields)
  3. Fase 3 — Mitigación: Confirmar línea base limpia, ejecutar ataque (prueba anterior), POST de cada dominio a /mitigated_domains, verificar mitigaciones aplicadas, volver a ejecutar ataque usando URLs http:// (prueba posterior), presentar comparación antes/después
  4. Fase 4 — Desmontaje (requiere confirmación humana explícita): Eliminar LB HTTPS → LB HTTP → origin pool → limpieza de zona DNS (solo registros manuales) → healthcheck → dominio protegido. No elimine la zona DNS.
TokenDescripciónPredeterminado
xF5XC_API_URLxURL de la API de la F5 XC Consolahttps://example-tenant.console.ves.volterra.io
xF5XC_API_TOKENxToken de credencial de API(proporcionado por el usuario)
xF5XC_EMAILxCorreo electrónico de notificación de CSDuser@example.com
xF5XC_NAMESPACExNamespaceexample-namespace
xF5XC_LB_NAMExNombre base del HTTP Load Balancer (crea ${name}-http y ${name}-https)example-lb
xF5XC_DOMAINNAMExFQDN a protegerapp.example.com
xF5XC_ROOT_DOMAINxDominio raíz (eTLD+1)example.com
xF5XC_ORIGIN_POOLxNombre del origin poolcsd-origin
xF5XC_ORIGIN_IPxIP del Servidor de origen44.232.69.192
xF5XC_ORIGIN_PORTxPuerto del Servidor de origen3000
xF5XC_HC_NAMExNombre del healthcheckcsd-hc

La especificación del HTTP Load Balancer usa grupos de elección oneOf donde exactamente una opción debe establecerse por grupo. Establecer cero o más de una opción en un grupo provoca un error 422.

Elecciones clave relacionadas con CSD:

Grupo de elecciónOpcionesPredeterminado de CSD
client_side_defense_choiceclient_side_defense, disable_client_side_defenseclient_side_defense
java_script_choice (anidado en CSD)disable_js_insert, js_insert_all_pages, js_insert_all_pages_except, js_insertion_rulesjs_insert_all_pages

Elección de tipo de escucha (HTTP vs HTTPS):

La demostración crea dos LBs con diferentes configuraciones de escucha:

LBElección de escuchaConfiguración
${F5XC_LB_NAME}-http (primario)http"http": { "dns_volterra_managed": true, "port": 80 }
${F5XC_LB_NAME}-https (secundario)https_auto_cert"https_auto_cert": { "http_redirect": true, "port": 443, ... }

Las claves http y https_auto_cert son mutuamente excluyentes — cada LB usa exactamente una.

Elecciones anidadas de auto-certificado HTTPS (solo LB secundario):

Grupo de elecciónOpcionesPredeterminado
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

Elecciones anidadas de single_lb_app (configuración ML):

El objeto single_lb_app tiene sus propios grupos oneOf requeridos. Establecer single_lb_app: \{\} sin estas elecciones anidadas provoca un error 400.

Grupo de elecciónOpcionesPredeterminado
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

Otras elecciones a nivel de LB (todas configuradas como deshabilitar/predeterminado):

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 — El token de API es inválido o ha expirado. Regenere en AdministraciónCredenciales.
  • 403 Forbidden — El token carece de permisos para el namespace. Verifique los enlaces de roles.
  • 404 Not Found — El nombre del namespace o del objeto es incorrecto. Enumere los objetos con GET /api/config/namespaces/\{namespace\}/\{object_type\}.
  • 409 Conflict — El objeto ya existe. Para los dominios protegidos, un 409 con “domain already exists (in uriList)” significa que el dominio raíz ya está registrado en el tenant — esta es una condición de éxito, no un error. Para otros objetos, elimine primero o use PUT para actualizar.
  • 422 Unprocessable Entity — La validación del esquema JSON falló. Causas comunes: elección oneOf faltante, múltiples elecciones establecidas en el mismo grupo, tipo de campo incorrecto. Verifique el mensaje de error para el campo específico.
  • Límite de objetos agotado (código de error 8, mensaje "Object kind {kind} has exhausted limits({N})") — El tenant ha alcanzado un límite de cuota de objetos. La API devuelve HTTP 200 con un cuerpo JSON de error que contiene "code": 8, no HTTP 429. Puede verificar proactivamente la capacidad de cuota antes de encontrar este error usando la API de Uso de Cuota (GET /api/web/namespaces/system/quota/usage?namespace=system). El comportamiento depende del tipo de objeto:
    • Healthcheck (límite ~150): No bloqueante — omita el Paso 1 de la Fase 1 y cree el origin pool sin referencia a healthcheck. CSD no depende del monitoreo de salud.
    • Endpoint (límite ~500): Bloqueante — los endpoints son sub-objetos creados dentro de los origin pools. Si se alcanza este límite, la creación del origin pool falla. Elimine origin pools no utilizados (lo que libera sus sub-objetos de endpoint) o contacte a su administrador para aumentar el límite del tenant.
    • Origin pool: Bloqueante — elimine origin pools no utilizados o contacte a su administrador.
    • HTTP load balancer: Bloqueante — elimine load balancers no utilizados o contacte a su administrador.
    • Protected domain: Bloqueante — elimine dominios protegidos no utilizados o contacte a su administrador.

Si el Paso 2 de la Fase 1 (Verificar que el Healthcheck está vinculado) muestra un array vacío []:

  1. Elimine el origin pool: DELETE /api/config/namespaces/{namespace}/origin_pools/{pool_name}
  2. Verifique que el healthcheck existe: GET /api/config/namespaces/{namespace}/healthchecks/{hc_name}
  3. Vuelva a crear el origin pool con la referencia correcta al healthcheck

LB bloqueado en VIRTUAL_HOST_PENDING_A_RECORD

Sección titulada «LB bloqueado en VIRTUAL_HOST_PENDING_A_RECORD»

Si el state del load balancer permanece VIRTUAL_HOST_PENDING_A_RECORD después del Paso 4 de la Fase 1:

  1. Verifique que la zona DNS existe (solo DNS gestionado de F5 XC):

    Ventana de terminal
    curl -s \
    -H "Authorization: APIToken xF5XC_API_TOKENx" \
    "xF5XC_API_URLx/api/config/dns/namespaces/system/dns_zones/xF5XC_ROOT_DOMAINx" \
    | jq '.metadata.name'

    Un 404 significa que la zona DNS no ha sido creada en F5 XC.

  2. Verifique allow_http_lb_managed_records — si la zona existe pero el LB está pendiente, los registros gestionados pueden estar deshabilitados:

    Ventana de terminal
    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'

    Si es false o null, habilítelo usando el comando PUT en Fase 1 Paso 4, Opción A.

  3. DNS externo — si F5 XC no es autoritativo, cree los registros A y ACME CNAME manualmente en su proveedor DNS (consulte Fase 1 Paso 4, Opción B).

  4. Verificar resolución — después de corregir el DNS, confirme:

    Ventana de terminal
    dig +short xF5XC_DOMAINNAMEx A

    Si el registro A se resuelve, el LB pasa a VIRTUAL_HOST_READY. Realice consultas cada 30 segundos, hasta 4 iteraciones (2 minutos en total). Si sigue siendo VIRTUAL_HOST_PENDING_A_RECORD después de 2 minutos, vuelva a verificar la propagación del DNS y reporte al operador.

CSD debe estar habilitado a nivel de tenant. Contacte a su administrador de F5 XC para habilitar la Defensa del lado del cliente para su tenant. Esta es una configuración a nivel de tenant que no se puede configurar mediante API.

  1. Verifique que CSD está habilitado a nivel de tenant (Fase 1 Paso 5)
  2. Confirme que el load balancer tiene client_side_defense establecido en la especificación
  3. Verifique que el endpoint de configuración JS devuelve un scriptTag
  4. Visite el dominio protegido en un navegador y vea el código fuente de la página para confirmar que el script está inyectado

El registro de dominio protegido devuelve 409

Sección titulada «El registro de dominio protegido devuelve 409»

Un 409 con “domain already exists (in uriList)” significa que el dominio raíz ya está registrado en el tenant. Los dominios protegidos son de ámbito de tenant — no pertenecen a ningún namespace individual. Esta es una condición de éxito: el dominio ya está protegido y no se necesita ninguna acción adicional. Continúe con el Paso 7 de la Fase 1.

Si el cert_state del LB HTTPS muestra AutoCertDomainRateLimited, significa que Let’s Encrypt ha aplicado límites de frecuencia a la emisión de certificados para este dominio. Esto es esperado en entornos de demostración donde la infraestructura se crea y destruye con frecuencia.

Impacto: El LB HTTPS no servirá tráfico hasta que se restablezca el límite de frecuencia (normalmente 1 hora). El LB HTTP no se ve afectado en absoluto — todo el tráfico de demostración continúa normalmente a través de http://.

Resolución: No se requiere ninguna acción para la progresión de la demostración. El LB HTTP (${F5XC_LB_NAME}-http) es el objetivo principal de la demostración y no depende del aprovisionamiento de certificados. Si se desea HTTPS, espere a que se restablezca el límite de frecuencia y el certificado se aprovisionará automáticamente.

Certificado bloqueado — Recreación limpia (opcional)

Sección titulada «Certificado bloqueado — Recreación limpia (opcional)»

Cuando el certificado del LB HTTPS está bloqueado en DomainChallengePending o PreDomainChallengePending durante más de 15 minutos, la ruta de recuperación más rápida es eliminar el LB HTTPS y volver a crearlo. Con la zona DNS ya configurada (registros gestionados habilitados), la recreación limpia normalmente produce CertificateValid en 5–7 minutos — a menos que haya un límite de frecuencia.

  1. Elimine el HTTPS Load Balancer: DELETE .../http_loadbalancers/${F5XC_LB_NAME}-https
  2. Espere 30 segundos para la limpieza de la plataforma
  3. Vuelva a crear el HTTPS Load Balancer (Fase 1 Paso 3)
  4. Monitoree el estado del certificado (Fase 1 Paso 7) — espere CertificateValid en 5–10 minutos

La especificación canónica de la API de F5 Distributed Cloud está disponible en:

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

Este ZIP contiene las especificaciones de OpenAPI 3.0 para todos los grupos de API, incluyendo ves.io.schema.views.http_loadbalancer, ves.io.schema.healthcheck, ves.io.schema.origin_pool y ves.io.schema.shape.csd. Use estas especificaciones para validar los payloads JSON y descubrir campos adicionales.