Transfert TCP Nginx basé sur le nom d'hôte
Avec la sortie de L'équilibrage de charge TCP pour la version de la communauté Nginx, je voudrais mélanger les données pass-through OpenVPN et SSL. La seule façon pour Nginx de savoir comment acheminer le trafic est via leur nom de domaine.
vpn1.app.com ─┬─► nginx at 10.0.0.1 ─┬─► vpn1 at 10.0.0.3
vpn2.app.com ─┤ ├─► vpn2 at 10.0.0.4
https.app.com ─┘ └─► https at 10.0.0.5
J'ai jeté un oeil aux guides TCP et à la documentation du module , mais cela ne semble pas bien référencé. Si quelqu'un peut me diriger vers la bonne direction, je serais reconnaissant.
3 réponses
Hypothèses
Si je vous comprends bien, vous voulez effectivement que nginx écoute à une seule adresse IP et une combinaison de ports TCP (par exemple, listen 10.0.0.1:443
), puis, en fonction de la caractéristique du trafic de flux TCP entrant, acheminez-le vers l'une des 3 adresses IP différentes.
Vous ne mentionnez pas explicitement comment vous vous attendez à ce qu'il différencie les 3 domaines différents en jeu, mais mon hypothèse est que vous supposez que tout est juste TLS, et que vous devez vouloir en employer sorte D'un mécanisme TLS SNI (Indication de nom de serveur) pour la différenciation basée sur le domaine.
Je crois que la documentation liée aux flux fournie à http://nginx.org/docs/ est assez autoritaire et exhaustif pour les modules en jeu (je lis tout cela ici, car apparemment il n'y a pas encore de place centrale pour le recoupement, par exemple, aucune référence du module "stream core" vers les sous-modules (et docs/stream/
redirige juste docs/
), ce qui est en effet le cas pour les sous-modules. assez déroutant, puisque des choses comme http://nginx.org/r/upstream {[28] } est seulement documenté pour s'appliquer à http
, sans aucune mention d'applicabilité à stream
, même si les directives sont à peu près les mêmes dans le fin):
- http://nginx.org/docs/stream/ngx_stream_core_module.html
- http://nginx.org/docs/stream/ngx_stream_access_module.html
- http://nginx.org/docs/stream/ngx_stream_limit_conn_module.html
- http://nginx.org/docs/stream/ngx_stream_proxy_module.html
- http://nginx.org/docs/stream/ngx_stream_ssl_module.html
- http://nginx.org/docs/stream/ngx_stream_upstream_module.html
Réponse
Note que chaque directive nginx, de chaque module, a un nombre limité de Context
applicables.
En tant que tel, malheureusement, il n'y a tout simplement pas de directive pour fouiner dans SNI ici!
Au contraire, c'est en fait documenté dans stream_core
cela, pour citer, " Different servers must listen on different address:port pairs.
", qui, comme vous pouvez le noter, est également contraire à la façon dont le listen
la directive fonctionne au sein de la plus-commune http_core
, et est une référence assez claire au fait qu'aucun type de support SNI n'est actuellement mis en œuvre pour le listen
dans stream
.
Discussion
En tant que point de discussion et suggestion de résolution, l'hypothèse que le trafic OpenVPN est juste TLS avec le SNI snoopable n'est pas nécessairement correcte (mais je ne suis pas trop familier avec OpenSSL ou SNI):
Considérez que même si SNI est passivement snoopable aujourd'hui, c'est clairement contraire à la promesse de TLS de garder la connexion sécurisée, et, en tant que tel, peut changer dans une future version de TLS.
Pour des raisons de discussion, si OpenVPN est juste en utilisant une connexion TLS, et si N'est pas en utilisant TLS pour authentifier les utilisateurs avec des certificats d'utilisateur (ce qui rendrait beaucoup plus difficile de MitM le flux, tout en portant toujours les données d'authentification tout le long), alors, théoriquement, si nginx avait avec Nginx (depuis
proxy_ssl
est déjà pris en charge dansstream_proxy
).
Plus important encore, je crois Qu'OpenVPN peut mieux être exécuté sur son propre protocole basé sur UDP, auquel cas, vous pouvez utiliser la même adresse IP et le même numéro de port pour une instance du protocole https basé sur TCP et un autre du protocole OpenVPN basé sur UDP sans conflit.
En fin de compte, vous pouvez demander, à quoi le module de flux serait-il utile de toute façon, alors? Je crois que son public cible serait, (0), load balancing HTTP/2
avec plusieurs upstream
les serveurs, basés sur le hash
de l'adresse IP du client, par exemple, et/ ou, (1), un remplacement plus simple et sans protocole pour stunnel
.
Ceci est maintenant possible avec l'ajout du module ngx_stream_ssl_preread ajouté dans nginx 1.11.5 et du modulengx_stream_map ajouté dans 1.11.2.
Cela permet à Nginx de lire le Client TLS Hello et de décider en fonction de l'extension SNI du backend à utiliser.
stream {
map $ssl_preread_server_name $name {
vpn1.app.com vpn1_backend;
vpn2.app.com vpn2_backend;
https.app.com https_backend;
default https_default_backend;
}
upstream vpn1_backend {
server 10.0.0.3:443;
}
upstream vpn2_backend {
server 10.0.0.4:443;
}
upstream https_backend {
server 10.0.0.5:443;
}
upstream https_default_backend {
server 127.0.0.1:443;
}
server {
listen 10.0.0.1:443;
proxy_pass $name;
ssl_preread on;
}
}
Comme @lochnair l'a mentionné, vous pouvez utiliser le module ngx_stream_map et la variable $server_addr pour résoudre ce problème. Voici mon exemple.
Mon adresse IP hôte est 192.168.168.22
, et j'utilise keepalived bound 2 adresse IP virtuelle à eth0
.
$sudo ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 5c:f3:fc:b9:f0:84 brd ff:ff:ff:ff:ff:ff
inet 192.168.168.22/24 brd 192.168.168.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.168.238/32 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.168.239/32 scope global eth0
valid_lft forever preferred_lft forever
$nginx -v
nginx version: nginx/1.13.2
$cat /etc/nginx/nginx.conf
...
stream {
upstream pod53{
server 10.1.5.3:3306;
}
upstream pod54{
server 10.1.5.4:3306;
}
map $server_addr $x {
192.168.168.238 pod53;
192.168.168.239 pod54;
}
server {
listen 3306;
proxy_pass $x;
}
}
Ainsi, je peux visiter différents services MySQL avec le même port 3306 via différents VIP. Tout comme visiter différents services HTTP avec le même port via diffrent server_name
.
192.168.168.238 -> 10.1.5.3
192.168.168.239 -> 10.1.5.4