les services sont déployés dans kubernetes avec une route http interne exprimée selon le pattern xxx..mon.intranet.fr (le host configuré sur le service kubernetes par le projet)
une route http externe (publique) est également définie dans un dispositif “global-dns” non géré par le projet selon le pattern xxx..mon.extranet.com
la route http externe est redirigée via un dispositif de firewall applicatif (waf) vers la route http interne; cette redirection n’est pas gérée par le projet non plus; la requête redirigée comporte en particulier un header x-forwarded-proto = https, un header x-forwarded-host = xxx..mon.extranet.com et un header host = xxx..mon.intranet.fr (le host configuré sur le service kubernetes par le projet)
Existant :
les services sont déployés dans kubernetes avec une route http externe exprimée selon le pattern xxx.mon.cluster.gke..gcp.renault.com; cette route n’est pas protégée par le waf
-> Est-il possible de prévoir un mécanisme qui tient compte du header x-forwarded-host s’il est présent à la place du host configuré sur le service kubernetes par le projet ? Ou bien une configuration Tomcat (RemoteIpValve ou équivalent) permettant de couvrir ce cas ?
Le problème…
This request concerns an up-to-date 6.3.9 Simplicité instance
and these are the steps to reproduce it:
Depuis un réseau externe…
Accéder à la page de login sur le host externe xxx..mon.extranet.com
Cliquer sur [Sign in with Simplicité]
La requête de connexion est adressée au host interne xxx..mon.intranet.fr
Le context URL est déterminé par tomcat en fct de ce que lui envoie le reverse proxy.
Dans une “bonne” configuration du ou des reverses proxies, Simplicité sait sur quelle(s) URL il est publiquement exposé sans avoir à faire de configuration particulière niveau Simplicité ou Tomcat.
Bref il faudrait regarder les headers HTTP envoyés par le reverse proxy dans les différents cas
En fait, on est dans une configuration nouvelle que je n’avais jamais rencontrée : le service est exposé sur une url interne (celle en xxx.mon.intranet.fr, c’est la seule que je configure) et est accessible par cette url depuis le réseau interne mais aussi accessible via une redirection depuis une route externe accessible depuis un réseau publique (route définie en global-dns et routée par un waf). Il n’y a qu’une définition d’exposition du point de vue kubernetes. Lorsque les utilisateurs chargent l’url externe, ils voient bien la page de login de Simplicité et l’url dans leur browser est bien l’url externe. La page servie est par contre générée avec l’url interne (le hostname configuré sur le service).
Comme on a déjà dépassé ma limite de compétence, je préfère ne pas en dire plus pour ne pas risque de dire trop de bêtises.
Quelle serait cette “bonne” configuration du ou des reverses proxies permettant de couvrir de cas de figue ?
@bmo peux tu nous indiquer de quelle(s) customisation(s) vous auriez besoin sur la configuration de la valve RemoteIpValve. On a pris l’option de ne rien mettre par défaut mais on peut ajouter des variables pilotables par var d’env sur Doker comme on le fait sur d’autres configurations Tomcat
Voici une synthèse des combinaisons de headers observées :
Accès direct (mon.intranet.fr)
Accès WAF (mon.extranet.com)
host (raw)
mon.intranet.fr
mon.intranet.fr
x-forwarded-host
absent
mon.extranet.com
x-envoy-external-address
xxx.xxx.xxx.xxx (client direct)
10.xxx.xxx.xxx (proxy WAF)
x-forwarded-for
absent
absent
RemoteAddr
xxx.xxx.xxx.xxx
xxx.xxx.xxx.xxx
Tel que je comprends la problématique, il faut ajouter une configuration de valve standard Tomcat RemoteIpValve avec :
hostHeader=“x-forwarded-host”
protocolHeader=“x-forwarded-proto”
remoteIpHeader=“x-envoy-external-address”
trustedProxies (liste/regex de proxies pour border le risque de host spoofing)
Comme je ne sais pas déterminer la “liste de confiance des proxies”, j’ai mis en place une gestion des modalités de déploiement via des variables CI/CD avec :
une variable PROXY_TRUST_MODE pouvant être valorisée à “calibration” ou “hardened” (ou d’autres modes ultérieurs)
une variable PROXY_[mode]_LIST avec une liste sécurisée d’IP ou range (à déterminer via la calibration)
Si la variable PROXY_TRUST_MODE n’est pas définie, conserver le server.xml d’origine.
Si la variable PROXY_[mode]_LIST n’est pas définie, conserver le server.xml d’origine.
Pour l’instant, j’ai ajouté ceci dans le fichier conf/server.xml
Je suis pour l’instant parti sur le principe que le contenu du server.xml peut changer sans information préalable. J’ajoute donc les éléments de configuration relatifs à la valve (+ logs de calibration de nos trustedProxies) via une logique de patch.
Je teste encore car ça ne fonctionne toujours pas :
OK j’ajoute les variables TOMCAT_REMOTEIP_* qui vont bien pour la valve dans les prochaines versions des images (à priori on va releaser des nouvelles révision v5 et v6 demain ou en début de semaine prochaine):
Afin de parfaire ma compréhension du problème et de bien cibler la cause racine, peux-tu me confirmer que :
le header x-envoy-external-address n’est pas standard/portable (il semble a minima spécifique à Envoy, voire à notre propre config DevOps Renault)
si à la place, on avait eu le header x-forwarded-for, tout aurait fonctionné sans nécessiter de retoucher le server.xml.
dès pull de la prochaine image annoncée, il me suffira de définir les variables d’environnement TOMCAT_REMOTEIP_* et je n’aurai plus à bidouiller le server.xml
J’ai bon ?
Que se passe-t-il si on ne reconfigure qu’une partie des variables TOMCAT_REMOTEIP_* ?
Oui X-Forwarded-For est la valeur habituelle (et, du coup, celle par défaut de la valve). L’autre je ne l’ai jamais vue mais ça correspond peut être à un standard
Et oui en mettant tout ou partie des vars d’env TOMCAT_REMOTEIP_* ça ne sera plus la peine de “bidouiller” les fichiers XML
Après qques tests les attributs relatifs aux proxies (internalProxies, trustedProxies) posent pb lorsqu’ils sont presents dans le bloc XML mais qu’on ne passe pas de valeur explicite => la syntaxe ${var:-} n’est visiblement pas interprétée par Tomcat dans ces cas particuliers comme “pas renseigné” (et donc valeur par défaut) mais comme “renseigné à vide”.
Dans les images Docker récentes la config RemoteIPValve a été déplacée dans le context.xml de la webapp, elle n’est donc plus dans le server.xml global de la configuration Tomcat
La raison est que certains clients n’utilisent pas nos images standard avec son Tomcat préconfiguré mais uniquement la webapp pour la déployer dans un Tomcat specifique (dans des images Docker “maison” ou sans Docker) => il était donc nécessaire que cette valve se situe plutôt dans la configuration de la webapp