如何让现有的缓存 Nginx 代理使用另一个代理绕过防火墙?

Phi*_*ßen 7 firewall nginx proxy cache

我的问题是关于使用 Nginx 作为另一个代理背后的代理。(有点混乱。)

我想设置 Nginx,以便它充当 npm 镜像的缓存代理服务器。这是链接:http : //eng.yammer.com/a-private-npm-cache/

在我的本地机器上,不受防火墙限制,以下配置工作正常:

proxy_cache_path /var/cache/npm/data levels=1:2 keys_zone=npm:20m max_size=1000m
inactive=365d;
proxy_temp_path /var/cache/npm/tmp;

server {
   listen 80;
   server_name classen.abc.lan;
   location / {
      proxy_pass http://registry.npmjs.org/;
      proxy_cache npm;
      proxy_cache_valid 200 302 365d;
      proxy_cache_valid 404 1m;
      sub_filter 'registry.npmjs.org' 'classen.abc.lan';
      sub_filter_once off;
      sub_filter_types application/json;
   }
}
Run Code Online (Sandbox Code Playgroud)

现在我想将它应用到位于附加防火墙后面的服务器。在日志中,我可以确认它访问了正确的上游 IP,但由于内部防火墙,请求失败。

我们有一个内部代理,我可以用它绕过防火墙,例如:

$ curl http://registry.npmjs.org
curl: (7) couldn't connect to host
$ http_proxy=http://proxy.abc.lan:1234/ curl http://registry.npmjs.org
... succeeds ...
Run Code Online (Sandbox Code Playgroud)

这个技巧不适用于 Nginx,因为它忽略了http_proxy环境变量。看了文档,还是想不通怎么修改配置,让它内部可以使用代理。

是否可以结合两种解决方案?重要的是缓存仍然有效,否则,您可以直接使用外部镜像 registry.npmjs.org。

也许,Nginx 应该使用内部代理(proxy.abc.lan)作为proxy_pass,但是内部代理如何知道应该将请求发送到外部 npm 镜像(http://registry.npmjs.org)?

更新 Lukas 答案

我尝试了 Lukas 解决方案:

rewrite ^(.*)$ "http://registry.npmjs.org$1" break;
proxy_pass http://proxy.abc.lan:1234;
Run Code Online (Sandbox Code Playgroud)

日志显示 URL 被重写,但导致重定向(由 触发curl classen.abc.lan/test-url):

2014/03/24 11:31:16 [notice] 13827#0: *2 rewritten redirect: "http://registry.npmjs.org/test-url", client: 172.18.40.33, server: classen.abc.lan, request: "GET /test-url HTTP/1.1", host: "classen.abc.lan"
Run Code Online (Sandbox Code Playgroud)

curl 调用的结果不是来自http://registry.npmjs.org的预期 JSON 字符串,而是由 Nginx 生成的 html 页面:

$ curl classen.abc.lan/test-url
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.4.7</center>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

jam*_*rty 5

Lukas 解决方案的问题是HttpRewriteModule,它会自动将前面带有 http(s) 的所有内容转换为 302。

如果您改为分两个阶段进行重写 - 第二个“中断” - 它应该可以工作。例如

rewrite ^(.*)$ "://registry.npmjs.org$1";
rewrite ^(.*)$ "http$1" break;
proxy_pass http://proxy.abc.lan:1234;
Run Code Online (Sandbox Code Playgroud)

我怀疑有一种更好的方法可以做到这一点,但它似乎有效。


Luk*_*kas 3

RFC 2616,第 5.1.2 节规定

当向代理发出请求时,绝对 URI 形式是必需的。
[...]
请求行示例如下:

  GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
Run Code Online (Sandbox Code Playgroud)

因此,您应该做的就是使用这些修改后的指令将请求传递给代理:

rewrite ^(.*)$ "http://registry.npmjs.org$1" break;
proxy_pass http://proxy.abc.lan:1234;
Run Code Online (Sandbox Code Playgroud)

根据nginx 文档, usingrewrite ... break;将强制 nginx 使用重写的 URI(现在是协议要求的绝对 URI),而不是尝试从指令构建它proxy_pass