向同一台机器发送请求时无法验证 SSL 证书

SSL Certificate is unable to be verified when sending request to the same machine

我有一台远程 Ubuntu 机器 运行 一个带有 next.js 的节点服务器,并使用 next-auth 进行身份验证。在本地使用 HTTP 一切正常。

配置

这是在 HTTPS 上运行节点服务器并使用 next.js.

的代码
const https = require('https');
const fs = require('fs');
const next = require('next')
const port = 3000;
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev, dir: __dirname })
const handle = app.getRequestHandler()
const { parse } = require('url');

const options = {
    key: fs.readFileSync('./path/to/private-key'),
    cert: fs.readFileSync('./path/to/csr'),
    ca: [fs.readFileSync('./path/to/gdroot-g2')]
};

app.prepare().then(() => {
    https.createServer(options, (req, res) => {
      const parsedUrl = parse(req.url, true);
      handle(req, res, parsedUrl);
    }).listen(port, err => {
        if (err) throw err
        console.log(`> Ready on localhost:${port}`)
    })
})

服务器在 3000 端口上运行,所以我使用 sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 3000 将请求重定向到 443。

我购买了注册为example.com的Deluxe SSL证书,还购买了域名,该域名有一条指向服务器IP的A记录。

这是服务器的 /etc/hosts 文件(不包括 IPv6 主机)。

127.0.0.1       localhost
192.XXX.XXX.XX server.example.com server

在我的 .env 中,我设置 NEXTAUTH_URL=https://example.com:3000 以便它向那里发送请求。

问题

在生产版本中,当 next-auth 尝试获取用户会话时,它会使用域名(例如:example.com)从服务器向自身发送 HTTPS 请求。服务器随后出现 request to https://mDomain.com:3000/api/auth/session failed, reason: unable to verify the first certificate.

错误

如果我更改 NEXTAUTH_URL=https://example.com(没有端口),节点会出现错误 request to https://example.com/api/auth/session failed, reason: connect ECONNREFUSED 192.XXX.XXX.XX:443

类似地,如果我使用 curl (curl https://example.com:3000/api/auth/session),curl 会以 curl: (60) SSL certificate problem: unable to get local issuer certificate 响应并且节点甚至从未收到请求(因此节点永远不会出错)。

请注意,如果我从我自己的机器上使用 curl,它会发送请求并且节点会收到它,但会立即抛出 request failed, unable to verify... 错误(我相信这是因为证书是从我的机器到服务器的,但随后从服务器到服务器的请求导致错误)。

我试过设置NEXTAUTH_URL_INTERNAL但错误是一样的(我试过设置为域名、IP地址和本地主机)。但是,我认为这不是问题的真正根源。

看来问题的根源是服务器在收到自己的HTTPS请求时,域名不匹配example.com(可能是来自127.0.0.1:3000或其ip地址),因此无法对其进行验证。当发件人是另一台机器时,所有 HTTPS 请求都有效。

我能想到的唯一解决方案是在服务器上为本地主机创建一个自签名证书(然后我可以指向 next-auth 在内部使用本地主机)。这将要求服务器使用 2 个 SSL 证书,这也可能是不可能的。然而,根据 next-auth 的说法,一切都应该与 HTTPS 一起正常工作,所以希望如此,我刚刚做了一个可修复的错误配置。

感谢您的帮助,如果您有任何解决方案,请告诉我。

更新

我对 NEXTAUTH_URL_INTERNAL 进行了更多试验,发现如果我在端口 3000 上启动一个 HTTPS 服务器并在端口 3001 上启动一个 HTTP 服务器并设置 NEXTAUTH_URL_INTERNAL=http://127.0.0.1:3001 它确实有效。

但是,在端口 3001 上有一个 HTTP 服务器(用户仍然可以连接到这个端口)并允许 next-auth 在内部使用 HTTP 不是一个安全漏洞吗?是否可以拥有 NEXTAUTH_URL_INTERNAL=https://127.0.0.1:3000 并为本地主机自行签署证书?或者这也是一个漏洞?

请注意,目前,如果没有本地主机的自签名证书,如果我设置 NEXTAUTH_URL_INTERNAL=https://127.0.0.1:3000,则节点错误 request to https://127.0.0.1:3000/api/auth/session failed, reason: Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the cert's list:。也许我可以将 localhost 添加到 altnames?或者甚至是服务器的 IP?

我最终通过 运行 另一个 HTTPS 服务器(用于同一个下一个应用程序)在一个单独的端口上修复了它,并为本地主机提供了 self-signed 证书。然后我设置 NEXTAUTH_URL_INTERNAL 以查询该端口上的本地主机。我还必须使用 mkcert 创建我自己的 CA。默认情况下,节点的提取无法识别我的自定义 CA,因此我将 NODE_EXTRA_CA_CERTS="/path/to/rootCA.pem 添加到 /etc/environment

所有这些都允许 next-auth 通过 HTTPS 查询本地主机并验证证书(即使它是 self-signed)。