Deux directions de trafic, une seule boîte
Bienvenue
La plupart des schémas d'architecture montrent le trafic dans un seul sens : le client en haut, le serveur en bas, la flèche pointant vers le bas. En réalité, le trafic circule dans les deux sens.
Trafic entrant (Ingress) : les clients externes accèdent à vos services via ce chemin. Un proxy inverse situé à la périphérie de votre réseau termine le TLS, achemine les requêtes et applique la politique d'accès.
Trafic sortant (Egress) : vos services accèdent aux services externes via ce chemin. Appeler l'API d'un processeur de paiement, récupérer une cible de webhook, envoyer une requête à un partenaire. Souvent via un proxy avant ou une passerelle NAT avec une liste d'autorisation.
De nombreuses architectures commencent par une seule boîte gérant les deux. Ça fonctionne, jusqu'au jour où ça ne fonctionne plus. Le mode de défaillance est subtil, ne se manifeste qu'après l'existence de suffisamment de services internes, & enseigne une leçon importante sur la séparation des préoccupations.
À la fin de cette leçon, vous comprendrez :
- Pourquoi l'ingress & l'egress représentent des schémas de trafic fondamentalement différents, avec des axes de mise à l'échelle & des modes de défaillance différents
- Le NAT hairpin & pourquoi un proxy qui tente de se connecter à lui-même échoue
- La bifurcation architecturale : une boîte devient deux, & ce que chacune d'entre elles possède alors exclusivement
- Les gains en isolation de sécurité : chaque côté peut verrouiller l'accès à ses pairs réellement autorisés
- Comment identifier quand votre conception à une seule boîte a franchi le seuil où la séparation est nécessaire
Pourquoi les directions exigent des outils différents
Deux types de charge de travail distincts à une même frontière réseau
Caractéristiques du trafic entrant (Ingress) :
- Initié par des entités externes (l'ensemble d'Internet)
- Le volume évolue en fonction de votre base d'utilisateurs
- Terminaison TLS, routage des requêtes, limitation de débit par source
- Préoccupations de défense en profondeur : DDoS, abus, extraction de données (scraping)
- L'IP publique doit accepter les connexions de n'importe qui
Caractéristiques du trafic sortant (Egress) :
- Initié par vos propres services (un ensemble connu et restreint de clients)
- Le volume évolue en fonction de vos schémas d'appel service à service et d'API externes
- Liste blanche d'adresses IP sources aux extrémités distantes (vous disposez d'une seule adresse IP de sortie fixe que les partenaires font confiance)
- Préoccupations de défense en profondeur : exfiltration de données, services internes compromis effectuant des appels sortants
- Doit rejeter les connexions provenant de toute entité autre que vos propres services
L'asymétrie clé : l'ingress accepte le trafic provenant du monde entier ; l'egress n'accepte le trafic que de vos propres services. Les placer sur la même machine signifie que cette machine doit être simultanément accessible depuis le monde entier (pour l'ingress) et accessible uniquement depuis vos services (pour l'egress). Les règles de pare-feu qui satisfont l'une nuisent à l'autre.
Le chemin de croissance : un petit projet peut masquer les deux rôles derrière une seule adresse IP et un seul outil, car le volume est faible et la liste blanche des adresses IP des partenaires est courte. À mesure que le projet grandit, la friction entre les deux rôles augmente, et un jour, un mode de défaillance spécifique (hairpin NAT) impose la séparation.
Le bug qui force la séparation
Une histoire de panne assainie
Imaginez une véritable bifurcation architecturale qui se produit dans les flottes de production. Les noms ci-dessous ont été modifiés ; la forme est identique à ce que les équipes rencontrent dans la réalité.
Une organisation exploite un unique serveur proxy à 203.0.113.5. Il gère l'ingress (port 443 pour les utilisateurs) & l'egress (port 1080 SOCKS5 pour les services internes appelant vers l'extérieur). Les services internes résident dans des sous-réseaux privés & acheminent tout le trafic sortant via ce proxy SOCKS5 sur 203.0.113.5:1080.
L'un des services hébergés derrière le même 203.0.113.5 est api.example.com. La résolution DNS publique de api.example.com pointe vers 203.0.113.5.
Maintenant, un autre service interne doit appeler api.example.com. Son chemin de sortie :
1. Le service interne résout api.example.com → 203.0.113.5
2. Le service interne envoie la requête via le proxy egress SOCKS5 situé à 203.0.113.5:1080
3. Le proxy tente d'ouvrir une connexion de lui-même vers 203.0.113.5:443
4. Connexion refusée. Le paquet devrait sortir puis réentrer le même NAT, ce que la plupart des piles réseau rejettent. Le proxy ne peut pas se connecter à lui-même via sa propre IP publique.
C'est le NAT en épingle de sécurité (hairpin NAT) : un paquet qui sort d'un NAT et doit réentrer dans le même NAT pour atteindre sa destination. Sans prise en charge spécifique du hairpin dans la couche de routage, le paquet est perdu.
Pourquoi le problème apparaît tard
Au début du projet, chaque service interne communiquait avec les autres services internes par nom d'hôte privé (internal-api.local) ou n'appelait pas les services publics de son propre organisme. Le chemin de hairpin n'existait tout simplement pas.
Puis, une nouvelle fonctionnalité a exigé que le service A appelle api.example.com (un nom d'hôte public). Le chemin de hairpin s'est activé. Connexion refusée. Panne.
La correction a traité le symptôme (forcer le résolveur à donner l'IP privée de api.example.com au lieu de l'IP publique). La cause racine : une seule machine faisait trop de tâches.
La fourche architecturale
Une boîte devient deux
La solution propre : séparer le proxy en deux machines.
Serveur d'entrée (IP publique 203.0.113.5) :
- Caddy / proxy inverse sur les ports 80, 443
- Les enregistrements DNS publics pointent ici
- Héberge api.example.com, app.example.com, etc.
Serveur de sortie (IP publique différente 203.0.113.99) :
- Proxy SOCKS5 / de transfert sur le port 1080
- Le pare-feu restreint les connexions entrantes aux adresses IP du sous-réseau interne uniquement
- Les services internes acheminent tout le trafic sortant via cette adresse
Ce que cela apporte :
1. Problème de hairpin résolu. Un service interne appelant api.example.com achemine le trafic sortant via 203.0.113.99 (sortie), qui se connecte ensuite normalement à 203.0.113.5 (entrée, une IP différente). La boucle NAT disparaît car les deux IP résident sur des machines différentes.
2. Isolation de sécurité. Le pare-feu du serveur de sortie peut être verrouillé sur un petit ensemble d'IP internes. Le pare-feu du serveur d'entrée reste ouvert au monde. Deux ensembles de règles distincts, chacun exprimant clairement un rôle.
3. Mise à l'échelle indépendante. La bande passante d'entrée évolue avec le nombre d'utilisateurs ; la bande passante de sortie évolue avec l'activité des services internes. On peut mettre à niveau l'un sans toucher à l'autre.
4. Isolation des pannes. Une mauvaise configuration de la sortie ne casse plus le site public. Un DDoS contre le site public n'épuise plus la bande passante de sortie.
5. Modèle mental plus clair. Chaque machine a un seul rôle. Les ingénieurs raisonnent sur les questions d'entrée sans penser à la sortie, et vice versa.
Deux axes, deux décisions de dimensionnement
Mise à l'échelle indépendante
Avant la séparation, la croissance dans l'une ou l'autre direction mettait sous tension la même machine. Après la séparation, chaque direction dispose de sa propre provision.
Dimensionnement de l'ingrès : évolue en fonction des utilisateurs. Les décisions de capacité résident dans la couche orientée public (plus de répliques de proxy inverse, VM plus grandes, CDN en amont). Le budget de bande passante est calculé en fonction du trafic utilisateur au pic.
Dimensionnement de l'égrès : évolue en fonction du volume d'appels de services internes vers des API externes. Souvent dominé par la livraison de webhooks, les appels aux processeurs de paiement ou les récupérations de données tierces. Le budget de bande passante est calculé en fonction des schémas d'appels internes.
Isolation des pannes : un DDoS contre l'ingrès public ne consomme plus la bande passante de l'égrès (ces appels au processeur de paiement passent quand même). Un plantage du proxy d'égrès ne met plus le site public hors service (les utilisateurs continuent d'accéder au site ; seules les appels sortants internes échouent).
SLO différents : la disponibilité de l'ingrès concerne les utilisateurs (panne visible du site) ; la disponibilité de l'égrès concerne les opérateurs (pannes en arrière-plan qui peuvent mettre plus de temps à être détectées). Chaque côté peut avoir son propre SLO.
Plusieurs serveurs d'égrès
Une fois le rôle d'égrès attribué à sa propre machine, le mouvement suivant évident est d'exécuter plusieurs machines d'égrès derrière un équilibreur de charge pour la haute disponibilité. Chaque nouveau service interne pointe vers le nom d'hôte d'égrès (qui se résout vers le pool équilibré) plutôt que vers une seule adresse IP.
Même leçon que pour le reste des systèmes distribués : une fois qu'une couche est sans état et a son propre rôle, elle se multiplie à moindre coût.
Une nouvelle intégration de partenaire
Votre organisation fonctionne avec la séparation entrée/sortie telle que conçue. Le serveur de sortie possède une IP publique fixe (203.0.113.99) que vous avez ajoutée à la liste blanche de trois API de partenaires existantes (un processeur de paiement, une passerelle SMS, un fournisseur d'e-mail).
Une équipe produit souhaite ajouter une quatrième intégration : un système de livraison de webhooks qui effectue des appels de rappel vers les points d'entrée des clients à travers le monde. Prévision du volume : 10 000 appels par minute, avec des pics jusqu'à 30 000.
Concevoir une frontière réseau pour un service en croissance
Synthèse
Vous avez appris pourquoi l'ingress et l'egress nécessitent des outils différents, l'échec du hairpin NAT qui impose cette séparation dans les flottes réelles, et comment l'échelle indépendante, l'isolation de sécurité et l'isolation des pannes s'accumulent une fois la séparation mise en place.
Appliquez les quatre.
Une société SaaS de taille moyenne exploite trois sous-domaines de produit (app, api, admin) pour ses utilisateurs, ainsi que quatre intégrations sortantes (Stripe, Twilio, SendGrid, un système de webhook client). Aujourd'hui, tout est derrière une seule machine proxy à une adresse IP publique unique. Ils ont commencé à recevoir des rapports d'échecs intermittents de hairpin lorsque les services internes tentent d'appeler api.example.com. Ils souhaitent concevoir une solution permanente.
Où va ce cours ensuite
Où va ce cours ensuite
Vous avez maintenant vu l'un des refactorings de séparation des préoccupations les plus propres dans les systèmes distribués : une machine devient deux, chacune avec un rôle clair, & le système hérite au passage des avantages en matière de mise à l'échelle, de sécurité & d'isolement des pannes.
La leçon suivante (cs_distsys_failure_modes_and_blast_radius) approfondit le raisonnement sur l'isolation des pannes. Vous lirez un post-mortem de DNS-SERVFAIL assaini, identifierez le motif de défaillance en cascade et rédigerez des mesures correctives sans recherche de coupable, ciblant les systèmes plutôt que les personnes.
Leçon complémentaire : geometry_of_ingress_egress_separation reformule la séparation en un graphe biparti et explore les sommets de coupure, les partitions de réseau et ce que la théorie des graphes vous apprend sur une frontière réseau.
Bien joué. En avant.