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

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

我的问题是关于使用 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;
   }
}

现在我想将它应用到位于附加防火墙后面的服务器上。在日志中,我可以确认它访问了正确的上游 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 ...

此技巧不适用于 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;

日志显示 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"

curl 调用的结果不是预期的 JSON 字符串http://registry.npmjs.org但是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>

答案1

Lukas 的解决方案存在以下问题HttpRewrite模块,它会自动将以 http(s) 开头的所有内容转换为 302。

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

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

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

答案2

RFC 2616,第 5.1.2 节状态

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

  GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

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

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

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

答案3

我认为它可能比上面的任何一个示例都简单。他们使用 rewrite 来重写 url,我认为您可以使用 proxy_pass,但将 url 传递给代理,将主机标头参数设置为您要去的位置。例如

http {

  upstream corporate_proxy  {
      server web-proxy.mycorp.com:8080;
  }

server {
   listen 80;
   server_name classen.abc.lan;
   location / {
      proxy_pass_header on;
      proxy_set_header Host "registry.npmjs.org";
      proxy_pass http://corporate_proxy;
      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;
   }
}

相关内容