更新:更改error_log为info

更新:更改error_log为info

如何配置 NGINX 以将外部请求路由到我的阶段和生产 Docker 主机

我有 2 个 FQDN stage.external.example.netexternal.example.net它们解析到同一个外部公共 IP 地址,例如140.240.40.111(在外部 DNS 上配置)

针对此 IP 的 HTTPS 请求(例如 GEThttps://stage.external.example.net) 然后被路由到我的 NGINX 服务器,该服务器有一条腿在非军事区例如,172.20.180.111另一个是我的内部网络,10.222.20.1/16其中我有2个docker主机,它们在内部进行如下解析:

  • stage.internal.example.net=>10.222.20.14
  • internal.example.net=>10.222.20.15

我正在尝试配置我的 NGINX 来路由:

  • 外部请求stage.external.example.net:443至内部stage.internal.example.net:443
  • 外部请求external.example.net:443至内部internal.example.net:443
stage.external.example.net:443 -> 140.240.40.111:443 -> 172.20.180.111:443 (NGINX) -> 10.222.20.14:443 (stage.internal.example.net)

external.example.net:443 -> 140.240.40.111:443 -> 172.20.180.111:443 (NGINX) -> 10.222.20.15:443 (internal.example.net)

我可以在 access.log 中看到请求到达了我的 NGINX,但是该请求似乎没有被转发到我的 docker 主机。

以下是我尝试过的配置,欢迎任何指点:

nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
  worker_connections 1024;
}

http {
  log_format main '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent"';

  access_log /var/log/nginx/access.log main;
  error_log /var/log/nginx/error.log warn; 

  sendfile            on;
  tcp_nopush          on;
  tcp_nodelay         on;
  keepalive_timeout   65;
  types_hash_max_size 2048;

  include             /etc/nginx/mime.types;
  default_type        application/octet-stream;

  include /etc/nginx/conf.d/*.conf;

  upstream internal_stage {
    server 10.222.20.14:443; # docker host stage.internal.example.net
  }

  upstream internal_production {
    server 10.222.20.15:443; # docker host internal.example.net 
  }

  # Forward all requests to stage.external.example.net:443
  server {
    listen 443;
    server_name stage.external.example.net;
    location / {
      proxy_pass http://internal_stage;
    }
  }

 
  # Forward all requests to external.example.net:443
  server {
    listen 443;
    server_name external.example.net;
    location / {
      proxy_pass http://internal_production;
    }
  }
}

更新:更改error_loginfo

我现在可以观察到我得到了400 Bad Request Client sent invalid request while reading client request line

答案1

似乎后端也https应该proxy_passhttps这样的:

location /upstream {
    proxy_pass https://backend.example.com;
}

请检查确保 http 流量上游

答案2

通过使用stream块和ssl_preread

nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
  worker_connections 1024;
}

http {
  log_format main '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent"';

  access_log /var/log/nginx/access.service-api.log main;
  error_log /var/log/nginx/error.service-api.log info;

  sendfile            on;
  tcp_nopush          on;
  tcp_nodelay         on;
  keepalive_timeout   65;
  types_hash_max_size 2048;

  include             /etc/nginx/mime.types;
  default_type        application/octet-stream;

  include /etc/nginx/conf.d/*.conf;
}

include /etc/nginx/passthrough.conf;

直通配置文件

stream {
  log_format basic '$remote_addr [$time_local] '
                 '$protocol $status $bytes_sent $bytes_received '
                 '$session_time "$upstream_addr" '
                 '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

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

  upstream internal_stage {
    server 10.222.20.14:443 max_fails=5 fail_timeout=300; # docker host stage.internal.domain.net
  }

  upstream internal_production {
    server 10.222.20.15:443 max_fails=5 fail_timeout=300; # docker host stage.internal.domain.net 
  }

  map $ssl_preread_server_name $upstream {
    stage.external.polylabs.net internal_stage;
    external.polylabs.net internal_production;
  }

  server {
    listen 443;
    proxy_pass $upstream;
    ssl_preread on;
    proxy_next_upstream on;        
  }
}

答案3

您的问题似乎将端口 443 与 HTTPS 混为一谈。如果您指的是端口 443 上加密的 HTTP,那么它就是 https,但 hostname:443 并未告诉我们有关协议或是否有加密的任何信息。这可能是否决该问题的原因。

当您使用“proxy_pass”时,nginx 会将 HTTP 请求转发到指定的地址 - 但 HTTP 明确支持在同一 IP 地址上多路复用不同的(虚拟)主机名。值得一提的是,TLS 中为主机名多路复用(SNI)实现了另一种补充方法,在这里您提供的配置与我推断的您的目标相冲突;在后端连接到 https。您的配置连接到 http。另一个原因可能是有人对您的问题投了反对票。但是 Nginx 的 proxy_pass 指令将自动处理 SNI 握手的重写,因此这不是您当前困境的原因。

话虽如此,除非您明确告诉 Nginx 不要这样做,否则它将验证后端服务器提供的证书。它是否使用由 nginx 验证的证书?您可以使用 openssl 的 s_client 从 nginx 主机对此进行测试。

该请求似乎没有被转发到我的docker主机。

你是如何得出这个结论的?(忽略这一点的理由可能是对你的问题投反对票的另一个原因)。

可能确实如此 - 但即使请求被转发到预期的 IP 地址/端口,如果后端认为它不是“stage.external.domain.net”的原始服务器,也会导致错误。

到目前为止,最简单的解决方案是告诉后端 Web 服务器它确实是“stage.external.domain.net”。但您也可以重写请求……

    server_name external.polylabs.net;
    location / {
      proxy_set_header Host "internal.polylabs.net";
      proxy_ssl_verify off;
      proxy_pass https://internal.polylabs.net;
    }

相关内容