Chaque ligne du journal d'accès affiche la même adresse de loopback, alors que la page se charge et que le certificat est validé correctement. Rien n'est cassé au sens d'inaccessible, et c'est pourquoi ce cas survit pendant des mois. Ce qui a cédé, c'est l'attribution : le backend web enregistre l'adresse du saut qui a transféré la connexion, et chaque décision ultérieure qui faisait confiance au journal a hérité de la même réponse erronée.

Les vérifications ci-dessous vont du câble vers l'intérieur. Arrêtez-vous à la première qui explique la valeur que vous voyez, car chaque étape suivante suppose que les précédentes sont déjà vraies. Aucune d'elles n'exige de deviner la configuration du front-end.

Suivez les deux connexions, pas la ligne du journal

Port-Share multiplexe deux protocoles sur un seul écouteur TCP. OpenVPN inspecte les premiers octets d'une connexion entrante, la conserve lorsqu'elle ressemble à son propre protocole et remet tout le reste à une seconde destination. Cette destination est généralement un serveur web sur le même hôte, si bien que la socket acceptée du serveur web a le processus OpenVPN pour pair.

ss -ltnp 'sport = :443'
ss -tnp 'dport = :8080'

La première commande montre qui possède l'écouteur public. La seconde montre les connexions qui arrivent sur le port du backend, et l'adresse de leur pair est celle que le journal d'accès imprimera. Si ce pair est loopback, le journal est exact et l'information s'est perdue une couche plus tôt. Renommer le champ du journal ou activer un module d'IP client ne récupérera pas une adresse que la connexion n'a jamais portée.

C'est la vérification qui décide de tout le reste. Si le pair du backend est l'adresse du navigateur, le partage de port n'est pas votre problème et le format du journal l'est.

Le transfert de transport n'est pas une injection d'en-tête

Le transféreur déplace des octets. Il n'analyse pas HTTP, donc il ne peut pas insérer un en-tête dans une requête qu'il ne lit jamais comme une requête, et il ne peut rien ajouter à un flux TLS qu'il laisse passer intact. Un module qui attend un en-tête de proxy continuera d'attendre celui qu'aucun composant ne produit.

Deux mécanismes peuvent porter l'adresse d'origine, et tous deux exigent un accord aux deux extrémités. Le protocole PROXY ajoute un petit en-tête au début de la connexion, et le backend doit être configuré pour l'attendre ; un backend qui ne l'attend pas traitera ces octets comme le début de la requête et échouera de manière déroutante. Les en-têtes de proxy de confiance comme X-Forwarded-For fonctionnent lorsqu'un proxy qui comprend HTTP les définit et que l'on indique au backend quels pairs peuvent les affirmer.

Aucun des deux n'est automatique, et aucun n'est sûr par défaut. Un backend qui croit tout en-tête transféré entrant permet à un client de choisir sa propre adresse, ce qui transforme une imprécision du journal en contournement du contrôle d'accès. Exécutez la démonstration une fois sur un hôte de test avant de faire confiance à un en-tête sur un chemin de production.

curl -sS -H 'X-Forwarded-For: 127.0.0.1' https://site.example.com/health
grep -F '127.0.0.1' /var/log/nginx/access.log | tail -n 3

Le répertoire optionnel de correspondance des sources

Le partage de port dispose d'un argument de répertoire optionnel qui enregistre une correspondance entre l'adresse source d'origine et la connexion de transfert, sur les plateformes où la fonction est implémentée. Le côté web est censé consulter cette correspondance ; ce n'est pas un en-tête et elle n'est pas injectée dans la requête.

Cette conception vaut la peine d'être connue car elle explique pourquoi certains déploiements signalent de vraies adresses et d'autres non, avec une configuration de front-end apparemment identique. La différence se situe côté réception, et elle réside généralement dans un module ou un correctif que la plupart des serveurs web empaquetés n'activent pas par défaut. Lisez la documentation de votre serveur web pour connaître le crochet exact qu'il propose et confirmez que le module est chargé au lieu de le supposer.

Si vous suivez cette voie, traitez-la comme une intégration en deux moitiés : la correspondance écrite par le transféreur et la recherche effectuée par le backend. Vérifiez les deux et confirmez que le répertoire survit à un redémarrage et ne croît pas sans limite sur un écouteur occupé.

Décidez quel composant répond de l'adresse

Avant de modifier la configuration, nommez le composant autorisé à affirmer une adresse client et les composants autorisés à le croire. Une seule réponse écrite évite la dérive habituelle, où trois couches différentes font confiance chacune à une source différente.

Trois conceptions couvrent la plupart des besoins. Gardez l'écouteur public et l'écouteur web séparés, afin que le serveur web voie de vrais pairs et qu'aucun problème d'attribution n'existe. Placez devant un proxy inverse qui comprend HTTP, terminez ou analysez la requête à cet endroit, et laissez-le définir un en-tête transféré pour un backend qui ne fait confiance qu'à ce proxy. Ou conservez le partage de port et acceptez que les journaux montrent le saut, avec des règles d'accès écrites contre l'identité de l'application plutôt que contre une adresse.

Ce qui ne fonctionne pas, c'est de traiter une adresse de loopback transférée comme la preuve que la requête a pris naissance sur cette machine. Cette inférence est exactement inverse : l'adresse est présente précisément parce qu'un composant local a établi la connexion. Une règle écrite ainsi admet quiconque peut atteindre l'écouteur.

Testez la règle, pas la page

Une page qui se charge prouve que le chemin de transfert fonctionne et ne dit rien de l'attribution. Testez le consommateur de l'adresse : la règle d'accès, la limite de débit, l'entrée d'audit, la recherche géographique.

curl -sS -o /dev/null -w '%{http_code}\n' https://site.example.com/
tail -n 5 /var/log/nginx/access.log
grep -c '' /var/log/nginx/access.log

Comparez une requête externe autorisée avec les enregistrements du front-end et du backend, et confirmez qu'une règle qui devrait refuser refuse toujours et qu'une qui devrait admettre admet toujours. Si la ligne du journal est correcte mais que la règle se comporte encore comme si tout le monde était local, la règle lit un autre champ que celui que vous avez corrigé.

Terminez en écrivant la chaîne d'attribution dans les notes de déploiement : quel écouteur, quel transféreur, quel en-tête ou correspondance, et quel backend est configuré pour lui faire confiance. La prochaine personne qui lira une ligne de journal aura besoin des quatre, et l'absence de cette note est la façon dont le même ticket se rouvre un an plus tard. Gardez-la à côté de la configuration plutôt que dans un fil de discussion.

Articles similaires