首页 > 解决方案 > Nginx:根据位置通过HTTPS到不同的目标

问题描述

我是 nginx 反向代理的完全菜鸟,如果这个问题很愚蠢,请原谅我。我的情况是我运行多个自行管理 HTTPS 证书的 docker 容器,我需要反向代理才能从浏览器访问它们。问题是证书需要在这些容器内进行管理,而不是从 nginx 反向代理。

我用谷歌搜索并尝试了很多东西,唯一有效的方法是将流直接传递到容器的本地地址。

events { }

stream {
  server {
    listen 443;
    listen [::]:443;

    proxy_pass 10.0.0.1:443;  
  }
}

当我使用流时(据我所知,这是通过所需的),我不知道如何区分我试图到达的目的地。

我需要使我能够执行以下操作的配置:

https://mail.example.com --> proxy_pass 10.0.0.1
https://mail.example.com --> proxy_pass 10.0.0.1
https://www.example.com --> proxy_pass 10.0.0.2 
https://foo.example.com --> proxy_pass 10.0.0.2

有没有办法做到这一点?

标签: dockernginxhttpsnginx-reverse-proxy

解决方案


您正在寻找的是如何设置 SSL 直通代理(TCP 转发)并使用 SNI 信息进行路由。我不确定你是否可以使用 nginx 来做到这一点,因为在 nginx 中没有办法使用 SNI 信息来管道连接(据我所知)。

我尝试为此使用 docker-compose 创建一个 HAproxy 配置,并且它似乎可以按您的意愿工作。我使用 docker-compose 在一个网络中运行所有应用程序,因此您可以根据 docker-compose 中的名称访问其他服务。

./docker-compose.yml

version: '3'

services:

  proxy:
    image: haproxy:1.9.8
    ports:
      - "443:443"
    volumes:
      - "./haproxy:/usr/local/etc/haproxy"

  mail:
    image: mail    # replace it with your app image, it must have port 443 open
    ports:
      - "445:443"  # this port mapping is for debug purposes only, remove it after

  foo:
    image: foo
    ports:
      - "446:443"

./haproxy/haproxy.cfg

defaults
  maxconn 1000
  mode http
  log global
  option dontlognull # bind *:443 ssl crt .
  timeout http-request 5s
  timeout connect 5000
  timeout client 2000000 # ddos protection
  timeout server 2000000 # stick-table type ip size 100k expire 30s store conn_cur

frontend https
  bind *:443
  mode tcp
  option tcplog
  tcp-request inspect-delay 5s
  tcp-request content accept if { req_ssl_hello_type 1 }
  use_backend foo-servers if { req.ssl_sni -i foo.example.com }
  use_backend mail-servers if { req.ssl_sni -i mail.example.com }

backend foo-servers
  mode tcp
  balance roundrobin
  option ssl-hello-chk
  server server1 foo:443       # You can use service name from docker-compose here

backend mail-servers
  mode tcp
  balance roundrobin
  option ssl-hello-chk
  server server1 mail:443

然后你可以运行它:

$ docker-compose up

然后确保您的应用程序正在运行:

$ curl -k https://localhost:445
mail service
$ curl -k https://localhost:446
foo service

最后我们可以测试我们的代理:

$ curl -k https://mail.example.com
mail service
$ curl -k https://foo.example.com
foo service

注意:为了让它工作,我将 mail.example.com 和 foo.example.com 添加到 /etc/hosts

127.0.0.1   mail.example.com
127.0.0.1   foo.example.com

PS。这是“foo”应用程序的 Dockerfile(相同的配置用于邮件):

./Dockerfile

FROM nginx:latest
COPY ssl.conf                   /etc/nginx/conf.d/ssl.conf
COPY mail.html                  /usr/share/nginx/html/index.html
COPY certs/mail.example.com.crt /etc/nginx/certs/
COPY certs/mail.example.com.key /etc/nginx/certs/
COPY certs/dhparam.pem          /etc/nginx/certs/

./ssl.conf

server {
    listen 443 http2 ssl;

    ssl_certificate       /etc/nginx/certs/mail.example.com.crt;
    ssl_certificate_key   /etc/nginx/certs/mail.example.com.key;
    ssl_dhparam           /etc/nginx/certs/dhparam.pem;

    root /usr/share/nginx/html;

    location / {
    }
}

创建自签名证书:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/foo.example.com.key -out certs/foo.example.com.crt
$ openssl dhparam -out certs/dhparam.pem 2048

构建图像:

$ docker build -t foo .

推荐阅读