Bastion Guacamole

Rôle

Bastion d'accès distant unique de l'infrastructure Indio : point d'entrée web (Apache Guacamole 1.6.0) pour l'administration RDP/SSH/VNC de pratiquement toute la flotte, sans nécessiter de client RDP/SSH local sur le poste de l'administrateur. VM guac-bastion / INDIDEN001 (10.100.2.244, seg-identite). À ce jour, il stocke 55 connexions couvrant vCenter, NSX Manager, les nœuds Edge, le witness vSAN, les deux contrôleurs de domaine AD (RDP), GitLab, Nexus, le nœud de contrôle lui-même, Horizon, et quasi toute la flotte IND* en SSH.

Contrairement aux autres briques identité de ce même dépôt (FreeIPA, voir FreeIPA & trust AD), l'application Guacamole a été installée manuellement par l'utilisateur — aucun projet IaC dédié, aucun rôle Ansible. Elle a été auditée en détail le 2026-07-12 ; cette page documente cet audit et son évolution depuis.

Remédiation la plus urgente de toute l'infrastructure Indio, non traitée à ce jour

Les identifiants par défaut du produit, guacadmin / guacadmin, fonctionnaient encore lors du dernier contrôle connu (2026-07-12, revalidé indirectement le 2026-07-16 lors de l'ajout d'une connexion — voir Procédure manuelle). Ce bastion donne accès à la quasi-totalité de l'infrastructure. Voir la section Points d'attention pour le détail complet et son statut de vérification.

Architecture

Pile applicative

Composant Port Bind Rôle
Apache httpd 443/tcp 0.0.0.0 (attendu — accès public du bastion) Reverse proxy, terminaison TLS, vhost durci
Tomcat local 127.0.0.1 (via AJP/HTTP proxifié par Apache) Exécute guacamole.war (webapp) + guacamole-auth-jdbc-mysql-1.6.0.jar
guacd 4822/tcp 0.0.0.0 (voir Points d'attention) Traducteur de protocole (RDP/SSH/VNC), non authentifié par nature
MariaDB 3306/tcp 0.0.0.0 (voir Points d'attention) Base guacamole_db : connexions, paramètres, historique, comptes
flowchart LR
    Client["Client (navigateur admin)"] -->|"HTTPS 443\nTLS auto-signé, SANS SAN"| Apache["Apache httpd\n(reverse proxy TLS, vhost durci)"]
    Apache -->|proxy local| Tomcat["Tomcat\nguacamole.war + auth-jdbc-mysql"]
    Tomcat -->|JDBC| MariaDB["MariaDB :3306\nguacamole_db\nbind 0.0.0.0"]
    Tomcat -->|"protocole Guacamole"| Guacd["guacd :4822\nbind 0.0.0.0, non authentifié"]
    Guacd -->|RDP| T1["addns-node01/02 (DC AD), win11..."]
    Guacd -->|SSH| T2["quasi toute la flotte IND*"]
    Guacd -->|"RDP/SSH/VNC"| T3["vCenter, NSX Manager, edges,\nwitness vSAN, GitLab, Nexus,\nHorizon, SOC (Kibana)..."]

Les 55 connexions, regroupées par zone

flowchart TB
    B["INDIDEN001 (guac-bastion)\n10.100.2.244 — seg-identite\nCompte unique : guacadmin"]
    B --> MGMT["Zone mgmt (10.15.100.x)\nvCenter-Server, nsx-manager,\nedge-node01/02/03, vSAN-Witness,\nfedora-control-node (nœud de contrôle lui-même)"]
    B --> AD["seg-identite — DC AD (RDP)\naddns-node01, addns-node02"]
    B --> DEVOPS["gitlab, nexus"]
    B --> VDI["horizon, win11"]
    B --> FLEET["Quasi toute la flotte IND* en SSH\n(dont INDSERV026, ajouté 2026-07-16)"]
    B -.->|"accès scopé grp-guac-bastion\najouté 2026-07-15"| SOC["seg-soc (deny-all sinon)\nKibana INDSOC017 + SSH"]

L'accès du bastion vers la zone SOC (deny-all scopé par défaut, voir NSX) est régi par des règles DFW dédiées, ajoutées le 2026-07-15 :

Règle Fichier Source Détail
guac-bastion-to-soc-ssh dfw_soc.tf grp-guac-bastion (IP 10.100.2.244 seule) SSH vers la zone SOC
guac-bastion-to-soc-kibana dfw_soc.tf grp-guac-bastion HTTPS vers Kibana (INDSOC017)

grp-guac-bastion est scopé à l'IP précise du bastion (ipaddress_expression = ["10.100.2.244"]), pas à toute seg-identite — un choix délibéré tant que le compte guacadmin par défaut reste actif (voir Points d'attention) : élargir l'accès à toute la zone identité aurait aussi élargi la portée d'une éventuelle compromission de ce compte partagé.

Aucun dépôt IaC dédié

Il n'existe pas de structure de dépôt à documenter pour l'application Guacamole elle-même — seule la VM est provisionnée en Terraform (voir ci-dessous). Toute la configuration applicative (Apache, Tomcat, guacd, MariaDB, schéma, comptes, 55 connexions) vit uniquement sur le système live et dans la base guacamole_db.

Provisioning Terraform

La VM INDIDEN001 est provisionnée par le même projet Terraform que FreeIPA (/root/identity/main.tf, voir FreeIPA & trust AD) : ressource vsphere_virtual_machine.vm en for_each sur var.nodes, qui inclut INDIDEN001 (2 vCPU / 4096 Mo) aux côtés des VM FreeIPA. Aucune ressource Terraform séparée ni spécifique à Guacamole — le clonage (template, réseau seg-identite, personnalisation IP/hostname) suit exactement le même mécanisme générique que le reste du dépôt.

Configuration Ansible

ansible/inventory.ini du même dépôt /root/identity déclare un groupe [guacamole] avec INDIDEN001 ansible_host=10.100.2.244, mais site.yml l'exclut explicitement :

# Note : le bastion Guacamole (INDIDEN001) est installé manuellement,
# hors de ce playbook (choix opérationnel).

Ce groupe d'inventaire existe donc mais n'est ciblé par aucun play — un lecteur pressé pourrait croire à une configuration Ansible fonctionnelle en voyant le groupe déclaré ; ce n'est pas le cas.

Procédure manuelle

1. Installation initiale (reconstituée depuis l'audit du 2026-07-12)

Aucun script ni playbook ne documente cette installation — ce qui suit est la reconstitution la plus complète disponible à partir de l'état système observé, pas une procédure rejouable automatiquement.

  1. VM clonée depuis tmpl-rocky96-hardened (SELinux enforcing, firewalld zone par défaut drop) via le Terraform partagé avec FreeIPA.
  2. Installation de guacamole-server (compilation/paquet fournissant guacd) et démarrage du service sur le port 4822.
  3. Installation de Tomcat + déploiement de guacamole.war (webapp Guacamole) et de l'extension guacamole-auth-jdbc-mysql-1.6.0.jar (authentification via base MySQL/ MariaDB — seule extension d'authentification chargée, pas de guacamole-auth-totp/duo).
  4. Installation de MariaDB, création de la base guacamole_db (import du schéma JDBC officiel), création d'un compte applicatif guacamole_user strictement scopé à cette base (pas de droits globaux), root@localhost laissé en authentification socket-only (refuse tout accès TCP même avec le bon mot de passe).
  5. /etc/guacamole/guacamole.properties configuré avec mysql-hostname, mysql-database, mysql-username/mysql-password (ce dernier réutilise le mot de passe standard de flotte — voir Points d'attention, valeur non reproduite ici) pointant vers l'extension JDBC.
  6. Apache httpd installé et configuré en reverse proxy TLS devant Tomcat : certificat auto-signé (CN=bastion.infra.indio, sans SAN, auto-émis — pas via la CA interne Vault, voir Vault & PKI), TLSv1.2/ TLSv1.3 uniquement, en-têtes de sécurité (HSTS, X-Frame-Options, etc.).
  7. firewalld : zone drop, seuls http/https/ssh explicitement autorisés.
  8. Compte guacadmin laissé à sa valeur par défaut du produit lors du premier démarrage — jamais changé depuis (voir Points d'attention).
  9. 55 connexions créées au fil du temps (vCenter, NSX Manager, edges, witness vSAN, DC AD en RDP, GitLab, Nexus, nœud de contrôle, Horizon, quasi toute la flotte IND* en SSH), organisées en groupes de connexion par segment/zone.

2. Procédure d'ajout d'une connexion (mécanisme réel, réutilisé au moins deux fois)

En l'absence de tout IaC pour ce composant, c'est littéralement la procédure de déploiement de ce composant au quotidien — utilisée telle quelle le 2026-07-16 pour ajouter INDSERV026 (SSH, groupe seg-service) et le 2026-07-15 pour l'accès SOC :

sequenceDiagram
    participant Op as Opérateur
    participant API as API REST Guacamole (INDIDEN001)
    participant DB as MariaDB (guacamole_db)
    Op->>API: POST /guacamole/api/tokens (compte administrateur)
    API-->>Op: authToken
    Op->>API: GET .../connectionGroups/ROOT/tree?token=...
    Note over Op: identifie le groupe de connexion du segment cible,<br/>récupère les parameters d'une connexion voisine du même segment
    Op->>API: POST .../connections?token=...<br/>{parentIdentifier, protocol, parameters dupliqués}
    API->>DB: INSERT connection + parameters
    API-->>Op: connexion créée
    Op->>API: DELETE /guacamole/api/tokens/{authToken}
    Note over API: token révoqué immédiatement après usage

Étapes concrètes :

  1. Identifier le groupe de connexion (connectionGroups) correspondant au segment NSX de la cible.
  2. S'authentifier (POST /guacamole/api/tokens) pour obtenir un token.
  3. Dupliquer les parameters d'une connexion voisine déjà existante dans le même segment (hostname/port/protocole diffèrent, le reste — options d'affichage, etc. — reste identique) plutôt que de tout ressaisir.
  4. Créer la connexion (POST /guacamole/api/connections).
  5. Révoquer le token immédiatement après usage (DELETE /guacamole/api/tokens/{token}) — pas de session admin laissée ouverte plus longtemps que nécessaire.

Procédure de déploiement

Il n'existe pas de procédure de déploiement automatisée de bout en bout pour ce composant :

  1. terraform apply dans /root/identity — provisionne la VM nue INDIDEN001 sur seg-identite (partagé avec FreeIPA).
  2. Installation applicative : entièrement manuelle, suivre Procédure manuelle §1 — aucun playbook Ansible ni script ne l'automatise.
  3. Ajout de nouvelles connexions : suivre Procédure manuelle §2.

Contrôle de santé / Vérification

# Services
systemctl is-active guacd tomcat9 mariadb httpd fail2ban firewalld

# Ports en écoute — vérifier en particulier le bind de guacd et MariaDB
ss -tlnp | grep -E ':4822|:3306|:443'

# Politique firewalld effective
firewall-cmd --get-default-zone
firewall-cmd --list-all

# fail2ban (attendu : inactif, cf. Points d'attention)
fail2ban-client status

# SAN du certificat servi réellement (pas seulement présent sur disque)
openssl s_client -connect 10.100.2.244:443 -servername bastion.infra.indio </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

# Test des identifiants par défaut (lecture seule stricte : le token est
# immédiatement révoqué après vérification, jamais utilisé pour agir)
TOKEN=$(curl -sk -X POST https://10.100.2.244/guacamole/api/tokens \
  -d 'username=guacadmin&password=guacadmin' | python3 -c "import json,sys;print(json.load(sys.stdin).get('authToken',''))")
[ -n "$TOKEN" ] && echo "guacadmin/guacadmin fonctionne encore" \
  && curl -sk -X DELETE "https://10.100.2.244/guacamole/api/tokens/$TOKEN"

Non ré-exécuté en direct lors de la rédaction de cette page

Dernière confirmation live connue : 2026-07-12 (audit complet) et 2026-07-16 (utilisation implicite du compte lors de l'ajout d'INDSERV026). Une tentative de re-vérification a été faite lors de la rédaction de cette page (2026-07-19) mais le nœud de contrôle utilisé pour cette session de documentation n'avait de route réseau que vers le segment management (10.15.100.0/26) — aucune route vers seg-identite ni vers la VIP Vault (10.100.2.130, elle aussi injoignable au même moment), confirmant une limitation de connectivité de cette session plutôt qu'un problème spécifique à ce composant. Aucune remédiation n'est mentionnée dans l'historique du projet entre le 07-16 et aujourd'hui — le finding doit être considéré comme toujours valide par défaut jusqu'à revérification.

Points d'attention

guacadmin / guacadmin — identifiants par défaut actifs (dernière confirmation : 2026-07-12)

Le compte administrateur par défaut du produit fonctionnait encore lors du dernier audit (POST /guacamole/api/tokens → HTTP 200 + authToken valide, révoqué immédiatement après test). Ce bastion stocke 55 connexions dont vCenter, NSX Manager, les nœuds Edge, le witness vSAN, les deux contrôleurs de domaine AD en RDP, GitLab, Nexus, le nœud de contrôle lui-même, Horizon, et quasi toute la flotte IND* en SSH — un accès à ce compte équivaut potentiellement à un accès à l'intégralité de l'infrastructure. Un seul compte Guacamole existe (guacadmin) : pas de comptes nominatifs, donc aucune imputabilité individuelle malgré la bannière SSH « toute connexion est journalisée ». C'est la remédiation la plus urgente actuellement identifiée dans toute l'infrastructure Indio.

  • remote_host toujours 127.0.0.1 dans guacamole_connection_history — le reverse-proxy Apache ne transmet pas l'IP client réelle à Tomcat (pas de RemoteIpValve/X-Forwarded-For exploité côté Tomcat). L'audit trail ne permet donc pas de savoir d'où vient réellement une connexion — gap d'auditabilité significatif pour un bastion dont la raison d'être est la traçabilité des accès.
  • Aucun enregistrement de session (recording-path) configuré sur aucune des 55 connexions — pas de rejouabilité a posteriori des sessions RDP/SSH passées par le bastion.
  • Pas de MFA/TOTP — seule l'extension guacamole-auth-jdbc-mysql-1.6.0.jar est chargée.
  • guacd (4822) et MariaDB (3306) écoutent sur 0.0.0.0 au lieu de 127.0.0.1 — protégés uniquement par firewalld (zone drop), pas par un bind localhost. Risque concret car la zone admin NSX est en DFW default-ALLOW (voir NSX) : toute VM compromise dans la zone admin pourrait tenter une connexion directe à guacd (protocole interne non authentifié par nature) ou à MariaDB, en contournant entièrement l'authentification web de Guacamole.
  • Le mot de passe MySQL de guacamole.properties réutilise le mot de passe standard de toute la flotte au lieu d'un mot de passe dédié à cette base applicative (valeur non reproduite ici, voir Gestion des secrets) — un secret partagé avec des dizaines d'autres usages pour une base qui, si elle fuite, exposerait aussi les paramètres de connexion (hôtes, identifiants) des 55 cibles.
  • Certificat TLS auto-signé sans SAN (CN=bastion.infra.indio) alors qu'une CA interne Vault existe déjà et pourrait émettre un certificat propre — voir Vault & PKI. Aucune raison technique de ne pas migrer ce vhost vers cette PKI.
  • fail2ban inactif — pas de protection anti-bruteforce sur le login web Guacamole (le SSH de la VM elle-même a MaxAuthTries 4 via le hardening standard du template, ce point-là est correct).
  • Points corrects/bien faits : SELinux enforcing, firewalld zone drop scopée, TLS moderne (TLSv1.2/1.3 uniquement) + en-têtes de sécurité (HSTS, X-Frame-Options, etc.) sur le vhost Apache, compte MariaDB applicatif strictement scopé à guacamole_db (pas de droits globaux), root@localhost MariaDB en authentification socket-only.
  • Accès réseau vers la zone SOC résolu le 2026-07-15 (voir Architecture) — pattern à réutiliser pour tout futur accès bastion → zone restreinte : un groupe NSX scopé à l'IP précise du bastion, jamais un élargissement de toute seg-identite, tant que le finding guacadmin ci-dessus reste ouvert.
  • Pattern d'ajout de connexion (Procédure manuelle §2) à documenter soigneusement à chaque usage tant qu'aucun IaC n'existe pour ce composant — c'est la seule trace de configuration qui subsiste en dehors de la base guacamole_db elle-même.