Aller au contenu

Démo

Ce guide vous accompagne à travers un exercice complet de Défense côté client sur F5 Distributed Cloud en utilisant l’API — structuré en quatre phases qu’un assistant IA ou un opérateur humain peut exécuter de bout en bout. Chaque étape inclut une commande curl prête à l’emploi avec des valeurs de substitution que vous pouvez personnaliser à l’aide du formulaire en haut de la page, d’un fichier .env, ou de tout outil d’Automatisation.

PhaseObjectifÉtapes
Phase 1 — ConstruireDéployer et valider l’infrastructure CSD complèteÉtapes 1–7
Phase 2 — AttaquerGénérer du trafic d’attaque simulé et confirmer que CSD l’a détectéÉtapes 8–9
Phase 3 — AtténuerPreuve avant/après atténuation — exécuter l’attaque, appliquer les atténuations, ré-exécuter l’attaque, comparerÉtapes 1–6
Phase 4 — DésinstallerSupprimer tous les objets de déploiement après confirmation expliciteDésinstallation

Avant de démarrer la Phase 1, vérifiez que l’environnement est propre. Exécutez ces vérifications API pour déterminer s’il reste des objets d’une exécution précédente :

Fenêtre 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
)
}'

Lorsque tous les objets de la Phase 1 existent (200) et que vous prévoyez de passer directement à la Phase 2, exécutez les commandes de vérification de la Phase 1 Étape 7 pour confirmer l’état de santé de l’infrastructure avant de sauter. Utilisez les commandes exactes de Phase 1 — Étape 7 : Vérifier :

  1. Résolution DNS : dig +short xF5XC_DOMAINNAMEx A
  2. État du LB HTTP : GET .../http_loadbalancers/xF5XC_LB_NAMEx-http dirigé vers jq '{state: .spec.state}' — doit afficher VIRTUAL_HOST_READY
  3. Configuration JS CSD : GET .../js_configuration — doit contenir scriptTag
  4. Statut CSD : GET .../status dirigé vers jq '{configured: .isConfigured, enabled: .isEnabled}' — les deux doivent être true

Toutes les vérifications requises (DNS-1, LB-1, CSD-1, CSD-2) doivent être RÉUSSIES avant de passer à la Phase 2. Si une vérification échoue, exécutez la Phase 1 à partir de l’étape en échec.

La vérification préalable ci-dessus vérifie que l’environnement est propre. La matrice de préparation ci-dessous vérifie que l’environnement est capable — que tous les prérequis, quotas, connectivité et services de la Plateforme sont en place pour une démo réussie. Exécutez cette matrice avant chaque réunion dans le cadre de l’étape de préparation.

Chaque vérification possède un identifiant de test, un niveau (T0–T5), des critères RÉUSSI/ÉCHOUÉ/AVERTISSEMENT, et un chemin de remédiation. Les niveaux sont séquentiels — un ÉCHEC dans un niveau antérieur bloque l’exécution des niveaux suivants.

NiveauCatégorieBloque la démo ?Objectif
T0Connectivité & AuthentificationOuiPeut-on atteindre la Plateforme et s’authentifier ?
T1Quotas & CapacitéOui (si à la limite)Y a-t-il de la place pour créer des objets de démo ?
T2Prérequis de la PlateformeOuiLes services au niveau du locataire sont-ils configurés ?
T3Santé de l’Serveur d’origineAvertissementL’application backend répond-elle ?
T4Environnement propreRemédiation automatiqueY a-t-il des objets résiduels d’une exécution précédente ?
T5Préparation des certificatsInformatifHTTPS fonctionnera-t-il, ou devons-nous planifier un mode HTTP uniquement ?

Ces vérifications confirment que l’hôte d’exécution peut atteindre l’API F5 XC et que les informations d’identification sont valides.

Fenêtre 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
)
}'
Fenêtre 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
)
}'
Fenêtre 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
)
}'

Des sondes non destructives testent les permissions de lecture et d’écriture pour chaque type d’objet dont la démo a besoin. La lecture est testée via GET sur les points de terminaison de liste. L’écriture est testée via DELETE sur des objets inexistants connus — l’API retourne 403 si le RBAC refuse l’opération, ou 404 si l’opération est autorisée mais que l’objet n’existe pas. Cette technique sans effet secondaire évite la création d’objets de sonde temporaires.

Fenêtre 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("; ")
)
}'

Ces vérifications interrogent l’API d’utilisation des quotas du locataire pour déterminer les limites, l’utilisation actuelle et la capacité restante pour chaque type d’objet dont la démo a besoin. Cela remplace les tests de sonde-et-suppression par un seul appel API en lecture seule qui rapporte des chiffres exacts.

Interrogez le point de terminaison d’utilisation des quotas à l’échelle du locataire et calculez un statut RÉUSSI/AVERTISSEMENT/ÉCHOUÉ déterministe pour chaque type d’objet dont la démo a besoin. Ce point de terminaison requiert le namespace system. Un seul appel API vérifie tous les quotas de niveau Plateforme en une seule fois.

Le portail définit un tableau demo_needs qui spécifie combien d’objets de chaque type la démo consommera, si le type est requis, et le minimum nécessaire pour que la démo continue. Le filtre jq compare remaining avec needed et calcule le champ status de manière déterministe — aucune interprétation de l’opérateur n’est requise.

Fenêtre 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

Résultat du portail — le champ gate est le verdict déterministe unique :

  • PASS — tous les types d’objets ont remaining >= needed. La démo peut continuer.
  • WARN — au moins un type a une capacité réduite mais le minimum pour continuer est atteint (par exemple, seulement 1 emplacement LB disponible au lieu de 2, ou le quota de healthcheck est épuisé). La démo peut continuer avec des limitations.
  • FAIL — au moins un type requis a remaining < min_proceed. La démo ne peut pas continuer tant que le quota n’est pas libéré.

Exemple de résultat (AVERTISSEMENT — endpoint à capacité, healthcheck presque plein) :

{
"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"
}
Valeur de gateAction
PASSPasser à PF-T1-4 (vérification du domaine protégé), puis T2
WARNNoter les limitations dans le rapport de préparation, continuer avec une capacité réduite
FAILArrêter — signaler quels types sont épuisés et les étapes de remédiation ci-dessous

Remédiation par type :

TypeRemédiation
healthcheckSupprimer les healthchecks inutilisés pour libérer de la capacité. La démo continue sans healthcheck (CSD n’en nécessite pas).
origin_poolSupprimer les pools d’origine inutilisés ou contacter votre administrateur pour augmenter la limite du locataire.
endpointSupprimer les pools d’origine inutilisés dans d’autres namespaces pour libérer la capacité des endpoints (les endpoints sont des sous-objets des pools d’origine), ou contacter votre administrateur.
http_loadbalancerSupprimer les équilibreurs de charge inutilisés ou contacter votre administrateur. Si seulement 1 emplacement est disponible, le LB HTTP (principal) sera créé mais le LB HTTPS (secondaire) sera ignoré.

Les domaines protégés CSD n’apparaissent pas dans l’API d’utilisation des quotas de la Plateforme. Utilisez une vérification basée sur une sonde : créez et supprimez immédiatement un domaine protégé de sonde.

Fenêtre 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

Solution de repli : Vérifications de quota basées sur des sondes

Section intitulée « Solution de repli : Vérifications de quota basées sur des sondes »

Si PF-T1-0 échoue (l’API d’utilisation des quotas renvoie 403, 404, ou un format inattendu), revenez aux vérifications de sonde-et-suppression pour les quotas de healthcheck, de pool d’origine, d’endpoint et d’équilibreur de charge. Ces vérifications créent un objet temporaire et le suppriment immédiatement — si la création renvoie le code d’erreur 8 avec “exhausted limits”, le quota est plein.

Chaque sonde de repli utilise le même schéma : créer un objet temporaire, calculer un statut déterministe à partir de la réponse, puis supprimer la sonde. Le champ status est PASS si l’objet a été créé (.metadata.name présent), WARN ou FAIL si le code d’erreur est 8 (limites épuisées), selon que le type est requis ou non.

Sonde de healthcheck (AVERTISSEMENT si épuisé — les healthchecks sont optionnels pour CSD) :

Fenêtre 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

Sonde de pool d’origine & d’endpoint (ÉCHEC si épuisé — les deux sont requis) :

Fenêtre 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

Sonde d’équilibreur de charge HTTP (ÉCHEC si épuisé — les LBs sont requis) :

Fenêtre 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

Ces vérifications vérifient les services au niveau du locataire dont la démo dépend.

Fenêtre 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
)
}'
Fenêtre 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
)
}'

À exécuter uniquement si PF-T2-2 a renvoyé 200 (la zone DNS F5 XC existe).

Vérifier l’état actuel :

Fenêtre 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)
}'

Remédiation automatique si nécessaire : Si le status est WARN ET que PF-T2-4 affiche les serveurs de noms F5 XC (ns1.f5clouddns.com, ns2.f5clouddns.com), activez automatiquement les enregistrements gérés en utilisant GET+PUT :

Fenêtre 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 .

Puis re-vérifiez pour confirmer que la mise à jour a pris effet :

Fenêtre 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'
RésultatAutorité DNSStatutRemédiation
trueQuelconqueRÉUSSILes enregistrements DNS gérés par LB seront créés automatiquement
false/nullF5 XCRemédiation automatiqueActiver via GET+PUT, vérifier, rapporter le résultat
false/nullExterneINFOLes enregistrements gérés ne sont pas applicables pour le DNS externe — la Phase 1 Étape 4 utilisera l’Option B (création manuelle d’enregistrements)
Remédiation automatique échouéeF5 XCÉCHECLe jeton peut manquer d’accès en écriture au namespace système — contacter l’administrateur du locataire
Fenêtre 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
)
}'

Ces vérifications vérifient que l’application backend est accessible.

Vérification de condition de saut : Calculez si l’adresse IP d’origine est une adresse TEST-NET RFC 5737 avant d’exécuter les tests de connectivité :

Fenêtre 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 est SKIP, enregistrez PF-T3-1 et PF-T3-2 comme SKIP et passez à T4. Sinon, exécutez les vérifications ci-dessous.

Fenêtre 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
)
}'

À exécuter uniquement si PF-T3-1 a renvoyé un statut HTTP valide :

Fenêtre 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"
RésultatStatutRemédiation
PASS: HTML contentRÉUSSIL’origine sert des pages HTML (requis pour l’injection JS CSD)
WARN: No HTML detectedAVERTISSEMENTL’origine peut être un service API uniquement ou renvoyer du non-HTML — l’injection JS CSD nécessite des réponses de pages HTML

Ces vérifications vérifient qu’aucun objet de configuration F5 XC nommé ne subsiste d’une exécution de démo précédente — équilibreurs de charge HTTP, équilibreurs de charge HTTPS, pools d’origine, healthchecks, domaines protégés et domaines atténués. T4 concerne le nettoyage au niveau des objets : si des objets API qui entreraient en conflit avec la création de la Phase 1 existent encore. Il ne teste pas les adresses IP, la connectivité réseau, ni la santé de l’origine (ces préoccupations relèvent de T3).

Exécutez les six commandes préalables de la section Vérification préalable ci-dessus. Appliquez la Logique de décision pour déterminer les prochaines étapes. Si des objets existent, le démontage automatique est effectué pendant l’étape de préparation (aucune confirmation nécessaire).

Vérifiez également et supprimez les objets de sonde obsolètes des exécutions préalables interrompues précédentes. Ces sondes ne sont créées que lorsque l’API d’utilisation des quotas est indisponible et que les vérifications de repli basées sur des sondes ont été utilisées, ou pour la sonde de domaine protégé (PF-T1-4) qui utilise toujours la vérification basée sur des sondes :

Fenêtre 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

Chaque DELETE renvoie 200 (vide {}) si l’objet existait, ou 404 s’il n’existait pas — les deux sont attendus.

Ces vérifications évaluent si HTTPS fonctionnera ou si la démo doit être planifiée en mode HTTP uniquement.

PF-T5-1 : Historique récent d’émission de certificats

Section intitulée « PF-T5-1 : Historique récent d’émission de certificats »

Vérifiez si un certificat Let’s Encrypt a été récemment émis pour le domaine de démo. Les cycles fréquents de création/destruction peuvent épuiser la limite de débit hebdomadaire (5 certificats en double par semaine par domaine).

PF-T5-2 : État du certificat du LB HTTPS existant

Section intitulée « PF-T5-2 : État du certificat du LB HTTPS existant »

À exécuter uniquement si un LB HTTPS existe d’une exécution précédente (PF-T4 a trouvé des objets) :

Fenêtre 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

Après avoir exécuté tous les niveaux, présentez un rapport de préparation consolidé :

## Préparation à la démo : PRÊT / PAS PRÊT / PRÊT AVEC AVERTISSEMENTS
### T0 : Connectivité & Authentification
| Vérification | Résultat | Statut |
|---|---|---|
| PF-T0-1 : Connectivité API | 200 | PASS |
| PF-T0-2 : Accès au namespace | 200 | PASS |
| PF-T0-3 : Accès à l'API CSD | 200 | PASS |
### T1 : Quotas & Capacité
| Vérification | Type | Limite | Utilisation | Restant | Nécessaire | Statut |
|---|---|---|---|---|---|---|
| PF-T1-0 : Portail de quota | `healthcheck` | 150 | 148 | 2 | 1 | PASS |
| PF-T1-0 : Portail de quota | `origin_pool` | unlimited | 420 | unlimited | 1 | PASS |
| PF-T1-0 : Portail de quota | `endpoint` | 500 | 498 | 2 | 1 | PASS |
| PF-T1-0 : Portail de quota | `http_loadbalancer` | unlimited | 116 | unlimited | 2 | PASS |
| PF-T1-0 : Portail de quota | **gate** | — | — | — | — | **PASS** |
| PF-T1-4 : Domaine protégé | — | — | — | — | 1 | PASS (probe) |
### T2 : Prérequis de la Plateforme
| Vérification | Résultat | Statut |
|---|---|---|
| PF-T2-1 : Statut du locataire CSD | configured + enabled | PASS |
| PF-T2-2 : La zone DNS existe | 200 | PASS |
| PF-T2-3 : Enregistrements DNS gérés | true | PASS |
| PF-T2-4 : Autorité des serveurs de noms DNS | f5clouddns.com | PASS |
### T3 : Santé de l'Serveur d'origine
| Vérification | Résultat | Statut |
|---|---|---|
| PF-T3-1 : Connectivité de l'origine | TEST-NET address (192.0.2.1) | SKIP |
| PF-T3-2 : Contenu HTML | TEST-NET address | SKIP |
### T4 : Environnement propre
| Vérification | Résultat | Statut |
|---|---|---|
| HTTP LB | 404 | PASS |
| HTTPS LB | 404 | PASS |
| Origin Pool | 404 | PASS |
| Healthcheck | 404 | PASS |
| Protected Domains | 0 | PASS |
| Mitigated Domains | 0 | PASS |
### T5 : Préparation des certificats
| Vérification | Résultat | Statut |
|---|---|---|
| PF-T5-2 : État du certificat | SKIP (no HTTPS LB) | INFO |
### Avertissements
- (lister tous les éléments WARN ou INFO avec leur contexte)

Règles de statut global :

ConditionStatut
Toutes les vérifications T0–T4 RÉUSSIESPRÊT
Toutes les vérifications T0–T4 RÉUSSIES mais T3 ou T5 ont des AVERTISSEMENTS/INFOPRÊT AVEC AVERTISSEMENTS
Une vérification T0, T1 ou T2 est en ÉCHECPAS PRÊT — résoudre avant de continuer
T4 a des objets résiduelsRemédiation automatique (démontage), puis re-vérifier

Cette section définit un flux de travail déterministe pour les assistants IA (Claude Code, Copilot, etc.) exécutant les étapes d’Automatisation des API. Suivre ce protocole élimine les conjectures — chaque point de décision a un chemin de résolution défini.

Résolvez chaque variable dans cet ordre exact. Arrêtez-vous à la première source qui fournit une valeur non-espace réservé :

  1. Vérifiez le fichier .env — recherchez .env à la racine du dépôt. S’il existe, analysez toutes les paires KEY=VALUE.
  2. Vérifiez l’environnement shell — exécutez env | grep F5XC_ pour trouver les valeurs déjà exportées dans la session courante.
  3. Identifiez les valeurs manquantes — comparez les valeurs résolues avec le tableau requis/optionnel ci-dessous. Une valeur est “manquante” si elle est absente, vide, ou encore définie sur une valeur par défaut de substitution (par exemple, example-api-token, example-tenant, example-namespace, app.example.com, user@example.com).
  4. Interrogez l’opérateur humain — pour chaque variable requise manquante, demandez à l’opérateur de fournir la valeur. Ne continuez pas tant que toutes les variables requises ne sont pas résolues.
  5. Appliquez les valeurs par défaut — pour chaque variable optionnelle manquante, utilisez la valeur par défaut du tableau ci-dessous sans interroger.

Chaîne vide = manquante : Une variable exportée avec une valeur vide (par exemple, F5XC_HC_NAME="") est traitée de la même manière qu’une variable non définie — appliquez la valeur par défaut. Utilisez l’expansion de paramètre shell (${F5XC_HC_NAME:-csd-hc}) pour appliquer les valeurs par défaut en une seule étape.

  1. Affichez la confirmation — montrez le tableau final des variables résolues à l’opérateur et attendez son approbation avant d’exécuter tout appel API.

Remplacement de l’étape de préparation : Pendant l’étape 1 de préparation, ignorez l’attente à l’étape 6. Affichez le tableau des variables résolues pour le registre, puis procédez immédiatement. Les étapes 1–5 s’appliquent toujours — si une variable requise est manquante après la vérification de .env et du shell, arrêtez et signalez les variables manquantes.

VariableRequisePar défautEspace réservé (traiter comme manquante)
F5XC_API_TOKENOuiexample-api-token
F5XC_API_URLOuihttps://example-tenant.console.ves.volterra.io
F5XC_NAMESPACEOuiexample-namespace
F5XC_DOMAINNAMEOuiapp.example.com
F5XC_ROOT_DOMAINOuiexample.com
F5XC_LB_NAMEOuiexample-lb-name, example-lb
F5XC_EMAILOuiuser@example.com
F5XC_HC_NAMEOptionnellecsd-hc
F5XC_ORIGIN_IPOptionnelle44.232.69.192
F5XC_ORIGIN_POOLOptionnellecsd-origin
F5XC_ORIGIN_PORTOptionnelle3000

L’assistant IA fonctionne dans l’un des trois modes suivants pendant la démo :

ModeQuand actifComportement
NormalPar défaut pendant Préparer, Exécuter, DésinstallerCommandes documentées verbatim uniquement
DébogageS’active automatiquement en cas d’échecDépannage créatif, mise à jour de la documentation
Questions/RéponsesPendant la phase Questions/RéponsesImprovisationnel — commandes ad-hoc autorisées pour répondre aux questions du public

Mode normal (par défaut) :

  • Chaque appel API, requête de vérification et commande shell doit provenir verbatim des fichiers de phase (Phases 1–4) ou de la section Vérification préalable ci-dessus
  • Substituez uniquement les espaces réservés xTOKENx par les valeurs de variables résolues
  • Ne construisez pas de points de terminaison API, de filtres jq, ou de commandes cURL à partir de connaissances générales ou d’inférences
  • Si une commande nécessaire n’est pas documentée, arrêtez et signalez à l’opérateur : “Cette étape de vérification n’est pas couverte par la documentation de phase”

Mode débogage (s’active automatiquement en cas d’échec) :

  • S’active automatiquement lorsqu’une commande documentée produit un résultat inattendu : réponse HTTP non-2xx, erreur d’analyse jq, délai d’expiration de commande, ou corps de réponse qui contredit le tableau de preuves
  • En mode débogage, l’assistant IA peut construire des commandes de diagnostic, inspecter les réponses API brutes, tester des variantes de points de terminaison, et utiliser le dépannage créatif pour trouver la cause racine
  • Préfixez toutes les sorties de débogage avec [DEBUG] afin que l’opérateur puisse distinguer l’activité de diagnostic de l’exécution normale
  • Documentez ce que vous apprenez : après avoir résolu un problème, mettez à jour le fichier de phase pertinent ou la section de dépannage avec :
    1. Le scénario d’échec (ce qui s’est mal passé)
    2. Ce qui a été essayé et N’A PAS fonctionné (afin que les exécutions futures ne le répètent pas)
    3. La résolution qui fonctionne (la commande ou la correction qui a résolu le problème)
  • L’objectif du mode débogage est de s’éliminer lui-même — chaque session de débogage doit produire une mise à jour de documentation qui rend la prochaine exécution entièrement déterministe
  • Une fois la documentation mise à jour et le problème résolu, revenez au mode normal et reprenez depuis la dernière étape documentée réussie
  • Si le mode débogage ne peut pas résoudre le problème, signalez les résultats à l’opérateur et arrêtez — ne continuez pas vers la phase suivante

Mode Questions/Réponses (pendant la phase Questions/Réponses) :

  • Actif uniquement pendant la phase de réunion Questions/Réponses, après la conclusion de la démo
  • L’assistant IA peut construire des appels API ad-hoc, exécuter des commandes de diagnostic, naviguer vers des pages non scénarisées, et modifier l’environnement de démo en direct pour illustrer les réponses aux questions du public
  • Pas de préfixe [DEBUG] — il s’agit d’un comportement improvisationnel intentionnel, pas d’une récupération d’erreur
  • Utilise la section Expertise produit CSD dans DEMO_EXECUTOR.md comme base de connaissances pour les questions sur les produits

L’accumulation d’initScript est une source courante d’échecs de démo. Chaque appel navigate_page avec un paramètre initScript ajoute le script à une liste persistante qui s’exécute à chaque chargement de document suivant. Suivez ces règles :

  • Naviguez toujours vers about:blank avant toute navigation avec initScript pour effacer les scripts accumulés des exécutions précédentes
  • Utilisez new_page avec isolatedContext lors du passage entre les phases de démo (par exemple, Phase 2 → Phase 3) pour garantir un état de navigateur complètement propre
  • Récupération de pages non réactives — si des délais d’expiration de take_screenshot ou take_snapshot se produisent, le contexte du navigateur est épuisé en ressources. Utilisez new_page avec isolatedContext pour créer un contexte frais, puis réessayez à partir de l’étape de navigation about:blank
  • Consultez les encadrés de la simulation d’attaque Phase 2 pour des conseils détaillés sur les défaillances d’origine transitoires et la récupération en cas d’épuisement des ressources

Après chaque appel API, l’assistant IA doit présenter des preuves structurées à l’opérateur humain en utilisant ce format :

Étapes de création (POST) :

ChampValeurStatut
Statut HTTP200PASS
Nom de l’objetcsd-origin
Propriété clé(extrait via jq)

Après chaque étape de création, exécutez un GET pour confirmer que l’objet existe et affichez ses propriétés clés. Si le GET renvoie 404, signalez ÉCHEC et arrêtez.

Étapes de vérification (GET/dig) :

TestRésultatStatut
DNS-1 : Enregistrement A198.51.100.10PASS
LB-1 : État du LB HTTPVIRTUAL_HOST_READYPASS
LB-2 : État du LB HTTPSVIRTUAL_HOST_READYINFO (optionnel)
TLS-1 : État du certificatCertificateValidINFO (optionnel)
CSD-1 : Balise JSscriptTag présentPASS

Référencez les identifiants de cas de test Diagnostics & Vérification (DNS-1, TLS-1, LB-1, CSD-1, etc.) comme norme de vérification pour chaque couche.

L’exécution des phases est séquentielle et conditionnée : chaque phase doit atteindre RÉUSSI sur toutes les vérifications requises avant que la phase suivante commence. La démo suit un cycle de réunion en quatre étapes — consultez la section Étapes de réunion dans DEMO_EXECUTOR.md pour les phrases déclencheurs et les règles comportementales.

L’assistant IA suit cette séquence :

  1. Préparer — résoudre les variables, exécuter les vérifications préalables, confirmer l’environnement propre (peut être exécuté séparément avant la réunion)
  2. Introduction — le SE se présente et énonce les objectifs de résultat (visibilité sur les menaces côté client, conformité PCI, détection en temps réel)
  3. Exécuter la Phase 1 (Étapes 1–7) — création et vérification de l’infrastructure ; toutes les vérifications de la Phase 1 doivent être RÉUSSIES avant de continuer
  4. Exécuter la Phase 2 (Étapes 8–9) — simulation d’attaque et vérification de détection via API ; les assistants IA avec Automatisation du navigateur exécutent les étapes de navigateur directement, les opérateurs sans outils de navigateur les effectuent manuellement
  5. Exécuter la Phase 3 — appliquer l’atténuation pour tous les domaines détectés, ré-exécuter l’attaque, vérifier que le blocage est efficace
  6. Conclusion — réitérer les objectifs de résultat, résumer les preuves de chaque phase, mettre en évidence les détections et atténuations clés
  7. Questions/Réponses — étape improvisationnelle, la démo reste active, le SE répond aux questions du public et pose des questions en retour
  8. Désinstallation (après la réunion) — Phase 4, confirmation explicite de l’opérateur requise, supprimer tous les objets dans l’ordre inverse de dépendance, confirmer l’environnement propre

Si une étape renvoie ÉCHEC, arrêtez et signalez l’échec avec le lien vers la section de dépannage appropriée avant de continuer.

  • Un jeton API F5 XC — générez-en un sous AdministrationCredentialsAPI Credentials
  • curl et jq installés localement
  • Un namespace avec les permissions pour créer des healthchecks, des pools d’origine et des équilibreurs de charge HTTP

Créez un fichier .env avec les valeurs de votre environnement. Un modèle est fourni dans le dépôt :

Fenêtre de terminal
cp .env.example .env

Modifiez .env avec vos valeurs réelles :

.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

Sourcez le fichier pour charger les variables dans votre session shell :

Fenêtre de terminal
set -a && source .env && set +a

Chaque espace réservé xTOKENx dans les commandes curl correspond directement à une variable d’environnement — par exemple, xF5XC_API_TOKENx correspond à $F5XC_API_TOKEN. Vous pouvez substituer ces valeurs à l’aide du formulaire interactif en haut de la page, ou laisser un assistant IA comme Claude Code lire votre .env et construire les commandes pour vous.

JetonPar défautDescription
xF5XC_API_URLxhttps://example-tenant.console.ves.volterra.ioURL de l’API XC Console
xF5XC_API_TOKENxexample-api-tokenJeton d’authentification API
xF5XC_EMAILxuser@example.comAdresse email de notification CSD
xF5XC_NAMESPACExexample-namespaceNamespace
xF5XC_LB_NAMExexample-lbNom de base de l’équilibreur de charge HTTP (crée ${name}-http et ${name}-https)
xF5XC_DOMAINNAMExapp.example.comFQDN à protéger
xF5XC_ROOT_DOMAINxexample.comDomaine racine (eTLD+1) pour le domaine protégé CSD
xF5XC_ORIGIN_POOLxcsd-originNom du pool d’origine
xF5XC_ORIGIN_IPx44.232.69.192Adresse IP du Serveur d’origine
xF5XC_ORIGIN_PORTx3000Port du Serveur d’origine
xF5XC_HC_NAMExcsd-hcNom du healthcheck

Cette section résume le flux de travail complet de l’exercice pour la création de scripts ou l’Automatisation.

  1. Clonez le dépôt et copiez le modèle d’environnement : cp .env.example .env
  2. Modifiez .env avec l’URL de votre locataire, le jeton API, le namespace et les valeurs de domaine
  3. Sourcez l’environnement : set -a && source .env && set +a
  4. Exécutez chaque phase dans l’ordre, en vérifiant RÉUSSI à chaque bloc de preuves avant de passer à la phase suivante

Les valeurs sont résolues en utilisant le protocole déterministe défini dans Protocole d’exécution de l’assistant IA :

  1. Fichier .env — analyser les paires KEY=VALUE depuis la racine du dépôt
  2. Environnement shell — vérifier env | grep F5XC_ pour les valeurs exportées
  3. Détection des espaces réservés — signaler toute valeur correspondant à un espace réservé par défaut (par exemple, example-api-token, example-namespace) comme manquante
  4. Interroger l’opérateur — demander chaque variable requise manquante
  5. Appliquer les valeurs par défaut — utiliser les valeurs par défaut intégrées pour les variables optionnelles manquantes
  6. Confirmer — afficher le tableau des variables résolues et attendre l’approbation de l’opérateur
  1. Phase 1 — Construire : Déployer l’infrastructure (healthcheck, pool d’origine, LB HTTP + LB HTTPS), configurer le DNS, activer CSD, enregistrer le domaine protégé, vérifier tous les Composants. Le LB HTTP est la cible principale de la démo ; le LB HTTPS est optionnel.
  2. Phase 2 — Attaquer : Exécuter la simulation d’attaque dans un navigateur en utilisant des URL http://, attendre 5–10 minutes, vérifier les détections via API (/scripts, /detected_domains, /formFields)
  3. Phase 3 — Atténuer : Confirmer une base de référence propre, exécuter l’attaque (preuve avant), POST chaque domaine vers /mitigated_domains, vérifier les atténuations appliquées, ré-exécuter l’attaque en utilisant des URL http:// (preuve après), présenter la comparaison avant/après
  4. Phase 4 — Désinstaller (nécessite une confirmation humaine explicite) : Supprimer le LB HTTPS → LB HTTP → pool d’origine → nettoyage de la zone DNS (enregistrements manuels uniquement) → healthcheck → domaine protégé. Ne pas supprimer la zone DNS.
JetonDescriptionPar défaut
xF5XC_API_URLxURL de l’API XC Consolehttps://example-tenant.console.ves.volterra.io
xF5XC_API_TOKENxJeton d’authentification API(fourni par l’utilisateur)
xF5XC_EMAILxEmail de notification CSDuser@example.com
xF5XC_NAMESPACExNamespaceexample-namespace
xF5XC_LB_NAMExNom de base de l’équilibreur de charge HTTP (crée ${name}-http et ${name}-https)example-lb
xF5XC_DOMAINNAMExFQDN à protégerapp.example.com
xF5XC_ROOT_DOMAINxDomaine racine (eTLD+1)example.com
xF5XC_ORIGIN_POOLxNom du pool d’originecsd-origin
xF5XC_ORIGIN_IPxAdresse IP du Serveur d’origine44.232.69.192
xF5XC_ORIGIN_PORTxPort du Serveur d’origine3000
xF5XC_HC_NAMExNom du healthcheckcsd-hc

La spécification de l’équilibreur de charge HTTP utilise des groupes de choix oneOf où exactement une option doit être définie par groupe. Définir zéro ou plus d’une option dans un groupe provoque une erreur 422.

Choix clés liés à CSD :

Groupe de choixOptionsPar défaut CSD
client_side_defense_choiceclient_side_defense, disable_client_side_defenseclient_side_defense
java_script_choice (imbriqué dans CSD)disable_js_insert, js_insert_all_pages, js_insert_all_pages_except, js_insertion_rulesjs_insert_all_pages

Choix de type d’écouteur (HTTP vs HTTPS) :

La démo crée deux LBs avec des configurations d’écouteur différentes :

LBChoix d’écouteurConfiguration
${F5XC_LB_NAME}-http (principal)http"http": { "dns_volterra_managed": true, "port": 80 }
${F5XC_LB_NAME}-https (secondaire)https_auto_cert"https_auto_cert": { "http_redirect": true, "port": 443, ... }

Les clés http et https_auto_cert sont mutuellement exclusives — chaque LB en utilise exactement une.

Choix imbriqués auto-cert HTTPS (LB secondaire uniquement) :

Groupe de choixOptionsPar défaut
portport (nombre)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

Choix imbriqués single_lb_app (configuration ML) :

L’objet single_lb_app possède ses propres groupes oneOf requis. Définir single_lb_app: \{\} sans ces choix imbriqués provoque une erreur 400.

Groupe de choixOptionsPar défaut
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

Autres choix au niveau LB (tous définis sur désactiver/par défaut) :

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 — Le jeton API est invalide ou expiré. Régénérez-en un sous AdministrationCredentials.
  • 403 Forbidden — Le jeton manque de permissions pour le namespace. Vérifiez les liaisons de rôles.
  • 404 Not Found — Le namespace ou le nom d’objet est incorrect. Listez les objets avec GET /api/config/namespaces/\{namespace\}/\{object_type\}.
  • 409 Conflict — L’objet existe déjà. Pour les domaines protégés, un 409 avec “domain already exists (in uriList)” signifie que le domaine racine est déjà enregistré sur le locataire — c’est une condition de succès, pas une erreur. Pour les autres objets, supprimez d’abord ou utilisez PUT pour mettre à jour.
  • 422 Unprocessable Entity — La validation du schéma JSON a échoué. Causes courantes : choix oneOf manquant, plusieurs choix définis dans le même groupe, mauvais type de champ. Vérifiez le message d’erreur pour le champ spécifique.
  • Limite d’objet épuisée (code d’erreur 8, message "Object kind {kind} has exhausted limits({N})") — Le locataire a atteint une limite de quota d’objets. L’API renvoie HTTP 200 avec un corps JSON d’erreur contenant "code": 8, pas HTTP 429. Vous pouvez vérifier proactivement la capacité des quotas avant de rencontrer cette erreur en utilisant l’API d’utilisation des quotas (GET /api/web/namespaces/system/quota/usage?namespace=system). Le comportement dépend du type d’objet :
    • Healthcheck (limite ~150) : Non bloquant — ignorez la Phase 1 Étape 1 et créez le pool d’origine sans référence de healthcheck. CSD ne dépend pas de la surveillance de la santé.
    • Endpoint (limite ~500) : Bloquant — les endpoints sont des sous-objets créés à l’intérieur des pools d’origine. Si cette limite est atteinte, la création du pool d’origine échoue. Supprimez les pools d’origine inutilisés (ce qui libère leurs sous-objets endpoint) ou contactez votre administrateur pour augmenter la limite du locataire.
    • Pool d’origine : Bloquant — supprimez les pools d’origine inutilisés ou contactez votre administrateur.
    • Équilibreur de charge HTTP : Bloquant — supprimez les équilibreurs de charge inutilisés ou contactez votre administrateur.
    • Domaine protégé : Bloquant — supprimez les domaines protégés inutilisés ou contactez votre administrateur.

Si la Phase 1 Étape 2 (Vérifier que le Healthcheck est lié) affiche un tableau vide [] :

  1. Supprimez le pool d’origine : DELETE /api/config/namespaces/{namespace}/origin_pools/{pool_name}
  2. Vérifiez que le healthcheck existe : GET /api/config/namespaces/{namespace}/healthchecks/{hc_name}
  3. Recréez le pool d’origine avec la référence de healthcheck correcte

Si l’état de l’équilibreur de charge reste VIRTUAL_HOST_PENDING_A_RECORD après la Phase 1 Étape 4 :

  1. Vérifiez que la zone DNS existe (DNS F5 XC géré uniquement) :

    Fenêtre 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 signifie que la zone DNS n’a pas été créée dans F5 XC.

  2. Vérifiez allow_http_lb_managed_records — si la zone existe mais que le LB est en attente, les enregistrements gérés peuvent être désactivés :

    Fenêtre 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 false ou null, activez-le en utilisant la commande PUT dans Phase 1 Étape 4, Option A.

  3. DNS externe — si F5 XC n’est pas autoritaire, créez les enregistrements A et ACME CNAME manuellement chez votre fournisseur DNS (voir Phase 1 Étape 4, Option B).

  4. Vérifiez la résolution — après avoir corrigé le DNS, confirmez :

    Fenêtre de terminal
    dig +short xF5XC_DOMAINNAMEx A

    Si l’enregistrement A est résolu, le LB passe à VIRTUAL_HOST_READY. Interrogez toutes les 30 secondes, jusqu’à 4 itérations (2 minutes au total). Si toujours VIRTUAL_HOST_PENDING_A_RECORD après 2 minutes, re-vérifiez la propagation DNS et signalez à l’opérateur.

CSD doit être activé au niveau du locataire. Contactez votre administrateur F5 XC pour activer la Défense côté client pour votre locataire. Il s’agit d’un paramètre à l’échelle du locataire qui ne peut pas être configuré via API.

  1. Vérifiez que CSD est activé au niveau du locataire (Phase 1 Étape 5)
  2. Confirmez que l’équilibreur de charge a client_side_defense défini dans la spécification
  3. Vérifiez que le point de terminaison de configuration JS renvoie un scriptTag
  4. Visitez le domaine protégé dans un navigateur et consultez le code source de la page pour confirmer que le script est injecté

L’enregistrement du domaine protégé renvoie 409

Section intitulée « L’enregistrement du domaine protégé renvoie 409 »

Un 409 avec “domain already exists (in uriList)” signifie que le domaine racine est déjà enregistré sur le locataire. Les domaines protégés sont à portée du locataire — ils n’appartiennent à aucun namespace unique. Il s’agit d’une condition de succès : le domaine est déjà protégé et aucune autre action n’est nécessaire. Continuez vers la Phase 1 Étape 7.

Si le cert_state du LB HTTPS affiche AutoCertDomainRateLimited, cela signifie que Let’s Encrypt a limité le débit d’émission de certificats pour ce domaine. C’est attendu dans les environnements de démo où l’infrastructure est fréquemment créée et détruite.

Impact : Le LB HTTPS ne servira pas de trafic jusqu’à ce que la limite de débit soit réinitialisée (généralement 1 heure). Le LB HTTP est complètement non affecté — tout le trafic de démo continue normalement via http://.

Résolution : Aucune action n’est nécessaire pour la progression de la démo. Le LB HTTP (${F5XC_LB_NAME}-http) est la cible principale de la démo et ne dépend pas de l’approvisionnement des certificats. Si HTTPS est souhaité, attendez que la limite de débit se réinitialise et le certificat sera auto-provisionné.

Certificat bloqué — Recréation propre (Optionnel)

Section intitulée « Certificat bloqué — Recréation propre (Optionnel) »

Lorsque le certificat du LB HTTPS est bloqué en DomainChallengePending ou PreDomainChallengePending pendant plus de 15 minutes, le chemin de récupération le plus rapide est de supprimer le LB HTTPS et de le recréer. Avec la zone DNS déjà configurée (enregistrements gérés activés), la recréation propre produit généralement CertificateValid en 5–7 minutes — sauf si limité en débit.

  1. Supprimez le LB HTTPS : DELETE .../http_loadbalancers/${F5XC_LB_NAME}-https
  2. Attendez 30 secondes pour le nettoyage de la Plateforme
  3. Recréez le LB HTTPS (Phase 1 Étape 3)
  4. Surveillez l’état du certificat (Phase 1 Étape 7) — attendez CertificateValid dans les 5–10 minutes

La spécification API F5 Distributed Cloud canonique est disponible à :

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

Ce ZIP contient les spécifications OpenAPI 3.0 pour tous les groupes d’API, y compris ves.io.schema.views.http_loadbalancer, ves.io.schema.healthcheck, ves.io.schema.origin_pool, et ves.io.schema.shape.csd. Utilisez ces spécifications pour valider les payloads JSON et découvrir des champs supplémentaires.