如何使用 nginx 作为具有多个 IP 和 SSL 的反向代理?

6 ssl nginx https reverse-proxy

我想设置具有多个域和 IP 的 nginx 反向代理,以便每个域使用不同的 SSL 证书。我将 Ubuntu 作为操作系统运行,该操作系统安装在 KVM/Qemu VM 上。

据我了解 nginx,它应该能够通过一个 IP 为一个域(以及属于它的子域)提供服务。但我无法让它运行......

这是我的 nginx 配置:

/etc/nginx/sites-enabled/my_first_domain

server {
  listen x.x.x.84:80;                                   # this is a public ip
  server_name firstdomain.com;

  access_log /var/log/nginx/access.log proxy;     # I made my own logformat
  error_log  /var/log/nginx/error.log;

  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Client-IP $remote_addr;
  proxy_set_header X-Host $host;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $remote_addr;

  location / {
    rewrite ^/(.*) https://firstdomain/$1;  # redirect to https
  }
}

server {
  listen x.x.x.84:443 ssl;                            # this is a public ip
  server_name firstdomain.com;

  ssl_certificate      /etc/nginx/ssl/combined.firstdomain.com.crt;
  ssl_certificate_key  /etc/nginx/ssl/wildcard.firstdomain.com.key;

  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Client-IP $remote_addr;
  proxy_set_header X-Host $host;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $remote_addr;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  location / {
    proxy_pass http://x.x.x.85;                       # this is a public ip, too 
    proxy_redirect off;
  }
}
Run Code Online (Sandbox Code Playgroud)

我认为这个配置非常简单。端口 80 上的每个请求都应该重定向到端口 443。第二个域的配置非常相似。

/etc/nginx/sites-enabled/anotherdomain

server {
  listen x.x.x.87:80;                                   # this is a public ip
  server_name anotherdomain.org;

  access_log /var/log/nginx/access.log proxy;     # I made my own logformat
  error_log  /var/log/nginx/error.log;

  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Client-IP $remote_addr;
  proxy_set_header X-Host $host;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $remote_addr;

  location / { 
    rewrite ^/(.*) https://anotherdomain.org/$1;  # redirect to https
  }   
}

server {
  listen x.x.x.87:443 ssl;                            # this is a public ip
  server_name anotherdomain.org;

  ssl_certificate      /etc/nginx/ssl/combined.anotherdomain.org.crt;
  ssl_certificate_key  /etc/nginx/ssl/wildcard.anotherdomain.org.key;

  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Client-IP $remote_addr;
  proxy_set_header X-Host $host;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $remote_addr;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  location / {
    proxy_pass http://x.x.x.89;                       # this is a public ip, too
    proxy_redirect off;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的 netstat -tulpen 片段:

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode       PID/Program name
...
tcp        0      0 x.x.x.84:80           0.0.0.0:*               LISTEN      0          8724        1187/nginx      
tcp        0      0 x.x.x.87:80           0.0.0.0:*               LISTEN      0          8723        1187/nginx      
tcp        0      0 x.x.x.84:443          0.0.0.0:*               LISTEN      0          8726        1187/nginx      
tcp        0      0 x.x.x.87:443          0.0.0.0:*               LISTEN      0          8725        1187/nginx      
...
Run Code Online (Sandbox Code Playgroud)

实际上,我认为在同一台服务器上使用 SSL 托管多个域应该足够了。但是 nginx 为每个请求提供相同的证书。结果是 SSL 错误。

还有另一种意想不到的行为。在调试时,我尝试使用 telnet 作为客户端获取网站。这个请求:

user@host:~$ telnet x.x.x.84 80
Trying x.x.x.84...
Connected to x.x.x.84.
Escape character is '^]'.
GET / HTTP/1.1
Host: firstdomain.com
Run Code Online (Sandbox Code Playgroud)

属于这个回复:

HTTP/1.1 302 Moved Temporarily
...
Location: https://firstdomain.com/
Run Code Online (Sandbox Code Playgroud)

嗯,那很好……但是这个请求 [同一个域(参见“主机:”-header),但 IP 现在不是正确的]:

user@host:~$ telnet x.x.x.87 80
Trying x.x.x.87...
Connected to x.x.x.87.
Escape character is '^]'.
GET / HTTP/1.1
Host: firstdomain.com
Run Code Online (Sandbox Code Playgroud)

...导致我要求的网站的交付。所以我通过代理获得了网站,尽管我将请求发送到错误的 IP 并且没有 SSL。这正是我想要阻止的!

谢谢你的想法!

Fle*_*der 5

您的第一个配置应该如下所示。

server {
  listen x.x.x.84:80;
  server_name firstdomain.com;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  return https://$server_name$request_uri;
}

server {
  listen x.x.x.84:443 ssl;
  server_name firstdomain.com;
  root ????;

  ssl_certificate      /etc/nginx/ssl/combined.firstdomain.com.crt;
  ssl_certificate_key  /etc/nginx/ssl/wildcard.firstdomain.com.key;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  location / {
    # Do not proxy everything to the backend, deliver static files
    # right away!
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Client-IP $remote_addr;
    proxy_set_header X-Host $host;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_redirect off;
    # The backend MUST be SSL enabled as well!
    proxy_pass https://x.x.x.85;
  }
}
Run Code Online (Sandbox Code Playgroud)

您的第二个配置应该如下所示。

server {
  listen x.x.x.87:80;
  server_name anotherdomain.org;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  return https://$server_name$request_uri;
}

server {
  listen x.x.x.87:443 ssl;
  server_name anotherdomain.org;
  root ????;

  ssl_certificate      /etc/nginx/ssl/combined.anotherdomain.org.crt;
  ssl_certificate_key  /etc/nginx/ssl/wildcard.anotherdomain.org.key;

  access_log /var/log/nginx/access.log proxy;
  error_log  /var/log/nginx/error.log;

  location / {
    # Do not proxy everything to the backend, deliver static files
    # right away!
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Client-IP $remote_addr;
    proxy_set_header X-Host $host;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_redirect off;
    # The backend MUST be SSL enabled as well!
    proxy_pass https://x.x.x.85;
  }
}
Run Code Online (Sandbox Code Playgroud)

请告诉我这是否有帮助,以便我们可以进一步重新定义配置。