在反向代理的Nginx上仅允许单个特定IP地址

3
我有一个运行在本地主机上的API。为了允许特定域路径下的外部访问API,我设置了反向代理。这部分工作正常。现在我正在尝试过滤访问并仅允许单个IP连接到API,换句话说,拒绝所有IP连接,除非是特定的IP。
使用以下配置,所有IP都被成功阻止,但它也阻止了我想要允许的那个IP。我已经进行了研究并尝试了几种解决方法,我怀疑我需要获取反向代理下的真实IP,但是还没有成功解决我的问题。非常感谢您的帮助。这是我的nginx配置文件代码:
server {

    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name foo.com www.foo.com;

    location / {

        allow XX.XX.XX.XX;
        #allow 127.0.0.1;
        deny  all;

        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

    }


    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/foo.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/foo.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

我希望允许的IP地址是XX.XX.XX.XX,实际上这是服务器的真实IP。但我认为这没有影响。我也尝试在“location / { }”范围内添加以下内容,但没有成功:

set_real_ip_from XX.XX.XX.XX;
real_ip_header    X-Forwarded-For;
real_ip_recursive on;

2
使用 ufw 来允许或拒绝 IP 地址。或者为了灵活性,修复您的代码以检查数据库中允许的 IP 地址。 - num8er
1
ufw无法解析主机名,因此无法根据请求的主机名进行智能防火墙设置。 - num8er
似乎您在IP解析方面遇到了问题。因为我刚刚测试了您的示例,它按预期工作。让我们启用访问日志并查看请求中获取的IP地址。在终端中运行 tail -f /var/log/nginx/access.log,然后在另一个终端或浏览器中尝试进行请求。或者禁用允许拒绝规则,只需添加 app.use((req, res, next) => {console.log(req.ip); next();}); 并捕获真实的IP地址。 - num8er
@num8er 好的,它能工作是因为我在描述/示例中搞砸了。它允许 127.0.0.1(本地主机)在反向代理中。在反向代理中,所有内容都来自本地主机,所以这个示例实际上是相反的问题:允许所有 IP。我已经添加了一条注释到这行代码中,以使示例与描述匹配。 - David Neto
如果您想允许所有IP,为什么不直接删除那些规则呢?但是allow ip-here; deny all;可以起作用。我认为您可能与X-Forwarded-For搞混了。 - num8er
显示剩余4条评论
1个回答

1
我认为将该控件放在Web服务器级别不是一个好主意。
授予权限更像是防火墙的任务。
但在某些情况下,当您想要动态地向注册主机提供访问权限而无需重新启动或配置某些内容时,最好在应用程序级别上设置守卫方法。


目前我可以推荐以下之一:

1)在应用程序或nginx前放置防火墙。您可以使用ufw

2)在应用程序级别设置访问控制。如果是nodejs应用程序,编写中间件:

middlewares/allowByIp.js

'use strict';

cons db = require('../database'); // mongoose models abstraction

const AllowedHosts = db.model('AllowedHost');

module.exports = async (req, res, next) => {

  const isAllowed = await AllowedHosts.findOne({ip: req.ip});
  if (!isAllowed) {
    res.status(403).send('Forbidden');
  }

  next();
};

或者:
'use strict';

cons allowedHosts = [... ip listing ...]; // take care of graceful restarting of Your app when You'll modify this array

module.exports = async (req, res, next) => {

  const isAllowed = allowedHosts.includes(req.ip);
  if (!isAllowed) {
    res.status(403).send('Forbidden');
  }

  next();
};

app.js 中:
const express = require('express');
const app = express();

const ipFirewall = require('middlewares/allowByIp');
app.use(ipFirewall);

...

app.listen(3000);


我已经检查了您的nginx示例,它按预期工作。

因此,我怀疑nginx获得了不同的IP。请检查/var/log/nginx/access.log以获取源部分请求目标时Nginx获得的真实IP地址。

但是,如果您想在内部限制访问,告诉服务器在请求foo.com时使用回环接口,则在/etc/hosts文件中添加这样一行:

127.0.0.1   foo.com www.foo.com

它会告诉您的服务器不要请求DNS服务器解析那个主机名,这将提供全局IP,并在外部请求时返回结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接