GCP 上从 http 重定向到 https 时出现 502

GCP 上从 http 重定向到 https 时出现 502

首先我想说的是,我知道关于这个问题有数百个主题,我之前也遵循过这些主题,以便让事情正常运转。然而,这个配置,我已经用了几个月,却无法在不同的环境中工作。

要求非常简单:将用户从端口 80 上的站点带到端口 443 上的同一站点。

我们已经说过,Apache 是前端,Tomcat 实例是我们实际服务所在的站点。这些服务托管在两台不同的机器上,一台是 GCP Compute Engine 上的虚拟机,另一台是最终将不再使用的裸机。

我们有自己的域名 example.com,其中包含服务索引。它由 Apache 2.4 静态提供。我们可以使用 Http 或 Https 访问它。从此索引,我们使用 proxy_mod 重定向到 app1、app2 和 app3。App2 和 App3 在我们的裸机服务器中。我们使用 DNS 通过其子域 app2.example.com/app2 和 app3.example.com/app3 将用户带到它们。我将在将来解决冗余问题。由于某些限制以及 app2 和 app3 尚未向公众开放的事实,我们只能通过 Http 访问它们。

由于 App1 与 Apache 服务器托管在同一 VM 中,因此它们具有相同的 SSL 证书。同样,我们可以使用 Http 和 Https 访问 example.com 和 example.com/app1。

现在的问题是:

正如标题所述,每当我使用之前尝试过的配置并在不同的环境中工作时,我都会遇到 502 错误的问题。所述配置大致如下:

<VirtualHost *:80>
    Redirect permanent /  https://bar.com/
</VirtualHost>
<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
 
        #SSL stuff
        SSLEngine On
        SSLCertificateFile /file.pem
        SSLCertificateKeyFile /file.key
        SSLVerifyClient none

        #Proxies
        ProxyRequests Off
        SSLProxyEngine on
        SSLProxyVerify none
        SSLProxyCheckPeerCN off
        SSLProxyCheckPeerName off
        SSLProxyCheckPeerExpire off
        ProxyPass        /api/          https://localhost:8443/api/
        ProxyPassReverse /api/          https://localhost:8443/api/
</VirtualHost>

bar.com 是我的测试环境。我采用了此配置,并针对 example.com 进行了如下调整。

<VirtualHost *:80>
    Redirect permanent /  https://example.com/
</VirtualHost>

<VirtualHost *:443>
        
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
 
        #SSL stuff
        SSLEngine On
        SSLCertificateFile      /etc/letsencrypt/live/example.com/fullchain.pem
        SSLCertificateKeyFile   /etc/letsencrypt/live/example.com/privkey.pem
        SSLVerifyClient none

        #Proxies
        SSLProxyEngine on
        SSLProxyVerify none
        SSLProxyCheckPeerCN on
        SSLProxyCheckPeerExpire on

        Redirect permanent      /app2      http://app2.example.com/app2
        Redirect permanent      /app3      http://app3.example.com/app3

        <Location /app1>
                ProxyPass               https://localhost:8443/app1
                ProxyPassReverse        https://localhost:8443/app1
        </Location>

</VirtualHost>

但这会导致 502 错误。再次,我知道端口 80 上的 vhost 没问题。我甚至读到过这是推荐的方法(目前正在查找我读到这篇文章的地方)。我没有尝试使用 mod_rewrite,因为我相信错误不是配置本身,而是 SSL 和 GCP 的代理模块混合在一起。

在 GCP 方面,我们有基本的防火墙规则,因为我们可以使用基本配置访问 Https 或 Http(即,我有 443 的虚拟主机,但在端口 80 上监听,没有第一个虚拟主机)。我设置了一个负载均衡器,但主要是为了利用 Google 的 CDN。它有一个后端组,其中有托管 Apache 和 app1 的 VM。对于前端,我为端口 80 和 443 都设置了规则,这意味着我有另一个专门用于负载均衡器的 SSL 证书,该证书由 Google 管理。我已经为两个端口设置了健康检查器,禁用和启用前端以确保与端口本身无关,到目前为止,这似乎不是问题。

我仍在思考整个 GCP 生态系统,因此我最好的猜测是,我遗漏了方程式的这一部分的一些东西,因为 Apache 配置本身对我来说似乎没问题。

我还没有尝试过:

  • 取下平衡器,设置没有静态 IP 的虚拟机并尝试。
  • 在前端和虚拟机中使用相同的证书。
  • 在前端禁用端口 80,并设置 Apache 来监听端口 443(我不想这样做,因为这更像是一种黑客行为;配置本身会像 http 一样工作)

为什么我还没有尝试过这些事情?好吧,正如你可能已经猜到的那样,在我们的实时服务器中摸索配置并不是一个真正的选择。我无法访问 bar.com,所以我无法随心所欲地尝试一些东西,而且使用 WSL 虽然有用,但并没有被证明与真实情况如此接近(这也可能是我的机器上的问题)。因为我只能在午夜前工作,所以我不能像我想的那样尝试很多事情。

有人知道是什么导致出现 502 错误吗?

如果您明白了但需要更多信息,请继续提问。我会尽可能详细地说明,但请记住,我仍在学习 IaaS 的工作原理。

编辑:

过了一会儿,我就可以进一步研究这个问题了。我使用的配置与我最初发布的第二个块代码相同。

回复@John Hanley(请注意,我将域名和 IP 更改为“通用”值):

使用curl -l -v该域,我得到以下信息:

*   Trying 1.1.1.1:80...
* TCP_NODELAY set
* Connected to example.com (1.1.1.1) port 80 (#0)
> GET / HTTP/1.1
> Host: exampole.com
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Content-Type: text/html; charset=UTF-8
< Referrer-Policy: no-referrer
< Content-Length: 332
< Date: Sun, 21 Nov 2021 08:57:31 GMT
<

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>
* Connection #0 to host example.com left intact

*   Trying 1.1.1.1:443...
* TCP_NODELAY set
* Connected to example.com (1.1.1.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=example.com
*  start date: Oct  4 13:25:53 2021 GMT
*  expire date: Jan  2 13:25:52 2022 GMT
*  subjectAltName: host "example.com" matched cert's "example.com"
*  issuer: C=US; O=Google Trust Services LLC; CN=GTS CA 1D4
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55b622463820)
> GET / HTTP/2
> Host: example.com
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 502
< content-type: text/html; charset=UTF-8
< referrer-policy: no-referrer
< content-length: 332
< date: Sun, 21 Nov 2021 08:57:56 GMT
< alt-svc: clear
<

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>
* Connection #0 to host example.com left intact

我认为 https 请求失败是因为缺少标头,因为我可以通过浏览器访问该网站。由此我可以看出请求正在由 LoadBalancer 正确处理(或者至少我是这样理解的),但该Connection stage changed消息让我很烦,特别是因为它需要几秒钟,就像等待响应一样。

apachectl -S

VirtualHost configuration:
*:80                   vm-name.region.gcp-project.internal (/etc/apache2/sites-enabled/0080-default.conf:1)
*:443                  example.com (/etc/apache2/sites-enabled/0443-secured.conf:6)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/var/log/apache2/error.log"
Mutex watchdog-callback: using_defaults
Mutex ssl-stapling-refresh: using_defaults
Mutex ssl-stapling: using_defaults
Mutex proxy: using_defaults
Mutex ssl-cache: using_defaults
Mutex default: dir="/var/run/apache2/" mechanism=default 
PidFile: "/var/run/apache2/apache2.pid"
Define: DUMP_VHOSTS
Define: DUMP_RUN_CFG
User: name="www-data" id=33
Group: name="www-data" id=33

这个很有趣。虽然端口 80 的 Vhost 引用 GCP 本身的 VM 实例,但端口 443 的 VHost 却没有。我不确定从中可以得到什么...我尝试使用 curl 通过其外部 IP 访问 VM。Http 显示永久重定向的正确状态,而 HTTPs 会由于 CN 和 SAN 不匹配而抛出错误(这是有道理的,因为证书是针对域的,而不是针对 IP)。有了这个,我有点确定将 VM IP 更改为我们拥有的静态 IP 并让 Apache 进行负载平衡将满足我的要求,但我们将在不久的将来添加至少一个 VM,因此现在这样做似乎...不好。我仍然认为 CDN 是一个很好的功能,毕竟应该使用它。

关于负载均衡器:

这个有点难以解释,所以我会尝试尽可能详细但直接(GCP CLI 中是否有东西可以显示此配置?还没有尝试过 CLI):

负载均衡器本身:

前端:

协议 港口 证书 SSL 策略
HTTP 80 - -
HTTPS 443 例如.com,由 GCP 管理 默认值

后端|端点协议|命名端口|超时|健康检查||-|-|-|-||HTTP|http|30 秒| hc-1(失败)| |HTTP/2|https|30 秒| hc-2(通过)|

*两个后端都指向同一个组,但端口不同。该组包含我们唯一的虚拟机。

主机/路径规则:

所有未损坏的都转到 HTTP 后端。对 /* 的请求转到 HTTPS 后端。

虽然 hc-1 失败了,但它实际上解析到了主页。但是当我尝试访问 app1 时,它失败了(这是有道理的,因为该 VHost 一开始不包含到 app1 的代理映射)。另一方面,hc-2 通过了,但访问网站会https://example.com引发 502 错误。

最后,关于如何启用站点,我使用了 a2ensite 作为 0080-default.conf 和 0443-secured.conf。

哦,还有,本周我们的网站无法正常工作。我想是周二,也就是 Spotify 出现问题的同一天。我不得不打开负载平衡器,进行任何更改,保存,撤消所述更改并再次保存才能使其再次正常工作,但从那时起,hc-1 就一直失败。

我一直在尝试对我的配置进行多种排列组合,从我知道有效的开始(也就是说,只有一个 HTTP 负载均衡器和端口 80 上的一个 VHost)。

相关内容