triage-leaked-infra

Par openshift · hypershift

Évalue si un VPC AWS ou un ensemble d'infra de HyperShift CI peut être supprimé sans risque. À utiliser quand l'utilisateur colle une sortie cleanleaked et demande « puis-je supprimer ça ? », « est-ce sans risque de retirer ça ? », « triage cette infra », pose des questions sur un verdict LEAKED ou UNCERTAIN, fournit un VPC ID ou un infraID et veut savoir s'il est orphelin, ou dit « vérifie ce VPC ». À utiliser également quand l'utilisateur demande « dois-je supprimer ça ? » à propos de n'importe quelle ressource AWS dans le compte HyperShift CI.

npx skills add https://github.com/openshift/hypershift --skill triage-leaked-infra

Évaluation des Fuites d'Infrastructure

Évaluer si un ensemble d'infrastructure AWS (VPC + ressources associées) de HyperShift CI est sûr à supprimer. Chaque affirmation doit être soutenue par une requête AWS empirique — ne jamais supposer, toujours vérifier.

Entrée

L'utilisateur fournit l'un des éléments suivants :

  • sortie de l'outil cleanleaked (un bloc d'ensemble infra)
  • Un ID VPC (par ex., vpc-0abc123...)
  • Un infraID (par ex., 00ab3695c5f73d4354b9 ou node-pool-78vcg)

Extraire l'infraID et l'ID VPC de l'entrée. Si un seul est fourni, dériver l'autre :

  • ID VPC → aws ec2 describe-vpcs --vpc-ids <VPC> --query 'Vpcs[0].Tags' → extraire infraID du tag kubernetes.io/cluster/<infraID> ou du tag Name (supprimer le suffixe -vpc)
  • infraID → aws ec2 describe-vpcs --filters "Name=tag:Name,Values=<infraID>-vpc" → récupérer l'ID VPC

Vérifications

Exécuter dans cet ordre. Pour chaque vérification, signaler PASS (signal sûr), FAIL (NE PAS supprimer), ou UNKNOWN (impossible à déterminer). Utiliser --region us-east-1 pour toutes les commandes.

1. Tags de protection

aws ec2 describe-vpcs --vpc-ids <VPC> --query 'Vpcs[0].Tags' --output json
  • Vérifier hypershift.openshift.io/do-not-delete=true → si présent : FAIL
  • Vérifier hypershift.openshift.io/ci-cluster → si présent : FAIL (ceci est un cluster de gestion)

2. Nom VPC protégé

Depuis le tag Name : si c'est hypershift-ci-2-vpc, hypershift-ci-3-vpc, ou hypershift-ci-metrics-vpc → FAIL

3. Utilisateur protégé

Vérifier si l'infraID contient l'un de ces noms d'utilisateurs : aabdelre, agarcial, ahmed, alamela, alesross, bclement, brcox, celebdor, cewong, dario, dari2o, dmace, glipceanu, jiezhao, jparrill, mbhalodi, mbrown, meha, mgencur, mulham, mraee, rkshirsa, sdminonne, sjenning, tsegura, vismishr

Si correspondance → FAIL (cluster développeur, contacter le propriétaire)

4. Date d'expiration / Âge

Depuis les tags VPC, vérifier expirationDate :

  • Si présent et pas encore expiré → FAIL
  • Si présent et expiré → PASS
  • Si absent → vérifier l'âge de la ressource à la place (le tag expirationDate n'existe que sur les ressources créées au cours des ~2 dernières semaines)

Quand expirationDate est absent, déterminer l'âge à partir de la sous-ressource horodatée la plus ancienne :

aws ec2 describe-vpc-endpoints --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'VpcEndpoints[*].CreationTimestamp' --output text
aws ec2 describe-network-interfaces --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'NetworkInterfaces[*].Attachment.AttachTime' --output text

Prendre l'horodatage le plus ancien trouvé. Si la ressource est plus ancienne que 24 heures ET la vérification du motif CI (étape 5) est PASS → PASS. Si plus jeune que 24 heures → FAIL. Si aucun horodatage trouvé → UNKNOWN.

5. Correspondance au motif CI

L'infraID correspond-il à un motif de test CI connu ?

  • ID Hex (20+ caractères hex minuscules, par ex., 00ab3695c5f73d4354b9) → CI générique e2e → PASS
  • Test nommé : create-cluster-*, node-pool-*, control-plane-upgrade-*, autoscaling-*, karpenter-*, karpenter-upgrade-control-plane-*, scale-from-zero-*, kms-verify-*, request-serving-*, private-*, proxy-*, spot-demo-*, ha-break-glass-creds-*, custom-config-*, ho-upgrade-*, multi-hop-upgrade-* → PASS
  • Aucun de ce qui précède (par ex., hc1-*, clust-*, dev-*, test-dev-*) → UNKNOWN (pourrait être un cluster développeur)

6. Liveness OIDC S3

aws s3api head-object --bucket hypershift-ci-oidc --key "<infraID>/.well-known/openid-configuration" --region us-east-1
aws s3api head-object --bucket hypershift-ci-2-oidc --key "<infraID>/.well-known/openid-configuration" --region us-east-1
aws s3api head-object --bucket hypershift-ci-3-oidc --key "<infraID>/.well-known/openid-configuration" --region us-east-1

Si l'un retourne 200 → FAIL (cluster toujours actif). Si tous retournent 404/erreur → PASS.

7. Instances EC2

aws ec2 describe-instances --region us-east-1 \
  --filters "Name=tag:kubernetes.io/cluster/<infraID>,Values=owned" \
           "Name=instance-state-name,Values=pending,running,stopping,stopped" \
  --query 'Reservations[*].Instances[*].[InstanceId,State.Name,Tags[?Key==`Name`].Value|[0]]' --output text

Si des instances retournées → FAIL. Si aucune → PASS.

8. Vérification red-hat-managed

aws ec2 describe-instances --region us-east-1 \
  --filters "Name=vpc-id,Values=<VPC>" "Name=tag:red-hat-managed,Values=true" \
  --query 'Reservations[*].Instances[*].InstanceId' --output text

Si des instances retournées → FAIL (infrastructure gérée ROSA). Si aucune → PASS.

9. Inventaire des sous-ressources (avec portée VPC)

Requêter chacune et signaler ID + Nom pour chaque ressource, pas seulement les comptages :

aws elbv2 describe-load-balancers --region us-east-1 --query "LoadBalancers[?VpcId=='<VPC>'].[LoadBalancerName,Type,Scheme,DNSName]" --output text
aws ec2 describe-vpc-endpoints --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'VpcEndpoints[*].[VpcEndpointId,VpcEndpointType,ServiceName,State]' --output text
aws ec2 describe-vpc-endpoint-service-configurations --region us-east-1 --output json | # filtrer par tag kubernetes.io/cluster/<infraID>
aws ec2 describe-nat-gateways --region us-east-1 --filter "Name=vpc-id,Values=<VPC>" --query 'NatGateways[*].[NatGatewayId,State,Tags[?Key==`Name`].Value|[0],NatGatewayAddresses[0].PublicIp]' --output text
aws ec2 describe-internet-gateways --region us-east-1 --filters "Name=attachment.vpc-id,Values=<VPC>" --query 'InternetGateways[*].[InternetGatewayId,Tags[?Key==`Name`].Value|[0]]' --output text
aws ec2 describe-subnets --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone,Tags[?Key==`Name`].Value|[0]]' --output text
aws ec2 describe-security-groups --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'SecurityGroups[*].[GroupId,GroupName]' --output text
aws ec2 describe-network-interfaces --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'NetworkInterfaces[*].[NetworkInterfaceId,InterfaceType,Description]' --output text
aws ec2 describe-route-tables --region us-east-1 --filters "Name=vpc-id,Values=<VPC>" --query 'RouteTables[*].[RouteTableId,Associations[0].Main,Tags[?Key==`Name`].Value|[0]]' --output text
aws ec2 describe-addresses --region us-east-1 --filters "Name=domain,Values=vpc" --output text | # vérification croisée avec les EIP des passerelles NAT

Cette vérification est informationnelle — toujours PASS mais lister chaque ressource avec son ID et son nom.

10. Zones Route53

aws route53 list-hosted-zones --output text --query 'HostedZones[*].[Id,Name,Config.PrivateZone,ResourceRecordSetCount]'

Rechercher les zones correspondant à <infraID>.ci.hypershift.devcluster.openshift.com et <infraID>.hypershift.local. Pour chaque zone trouvée, lister ses enregistrements :

aws route53 list-resource-record-sets --hosted-zone-id <ZONE_ID> --query 'ResourceRecordSets[*].[Name,Type]' --output text

Signaler les IDs de zone, les noms, privé/public, et le compte des enregistrements. Informationnelle — toujours PASS.

11. Fournisseur OIDC IAM

aws iam list-open-id-connect-providers --output json

Rechercher les fournisseurs dont l'ARN contient l'infraID (motif : oidc-provider/hypershift-ci-*-oidc.s3.*.amazonaws.com/<infraID>). Signaler si trouvé — ceci est une ressource IAM orpheline qui devrait être nettoyée avec l'ensemble infra.

12. Rôles IAM (par préfixe infraID)

aws iam list-roles --query "Roles[?starts_with(RoleName, '<infraID>')].[RoleName,CreateDate]" --output text

Signaler tout rôle IAM dont le nom commence par l'infraID. Ce sont des rôles orphelins de type OIDC (cloud-controller, ebs-csi, ingress, etc.) qui appartiennent à cet ensemble infra.

Sortie

Présenter le rapport dans ce format :

## Évaluation : <infraID>

| # | Vérification | Résultat | Détail |
|---|-------|--------|--------|
| 1 | Tags de protection | PASS | Pas de tag do-not-delete ou ci-cluster |
| 2 | Nom VPC protégé | PASS | Le nom est "abcdef1234-vpc" |
| 3 | Utilisateur protégé | PASS | Pas de correspondance de nom d'utilisateur |
| 4 | Date d'expiration | PASS | Expiré 2026-07-03 (il y a 5 jours) |
| 5 | Correspondance au motif CI | PASS | ID Hex (e2e-générique) |
| 6 | Liveness OIDC S3 | PASS | Non trouvé dans aucun bucket |
| 7 | Instances EC2 | PASS | 0 instances |
| 8 | red-hat-managed | PASS | Aucune instance gérée |
| 9 | Sous-ressources | INFO | 1 IGW, 1 subnet, 1 RTB |
| 10 | Zones Route53 | INFO | 2 zones trouvées |
| 11 | Fournisseur OIDC IAM | INFO | 1 fournisseur orphelin trouvé |
| 12 | Rôles IAM | INFO | 0 rôles orphelins |

### Verdict : SÛR À SUPPRIMER
Cet ensemble infra n'a pas de tags de protection, aucune instance en cours d'exécution, aucun document OIDC,
et l'infraID correspond à un motif CI hex. Tous les contrôles de suppression réussissent.

Règles de verdict

Ces règles sont non négociables :

  • Tout FAIL → verdict est NE PAS SUPPRIMER (expliquer quel contrôle a échoué et pourquoi)
  • Tous les contrôles PASS et correspondance au motif CI → SÛR À SUPPRIMER
  • Tous les contrôles PASS mais motif CI est UNKNOWN → INCERTAIN (pourrait être un cluster développeur)
  • Si motif CI est PASS et âge/expiration est PASS (plus ancien que 24h ou expiré), les contrôles UNKNOWN restants ne bloquent pas → SÛR À SUPPRIMER (une ressource CI confirmée plus ancienne que 24h sans OIDC, sans instances, et sans tags de protection est en fuite)
  • Si motif CI est UNKNOWN et N'IMPORTE QUEL contrôle est UNKNOWN → INCERTAIN — DÉCISION HUMAINE REQUISE

Skills similaires