简体   繁体   中英

Allow only a single specific IP address on Nginx under reverse proxy

I have an api running on localhost. To allow external access to the API under a specific domain path I've set a reverse proxy. This part works fine. Now I'm trying to filter access and allow only a single IP to connect to the API, in other words, deny all IP's connections except from a specific one.

With the configuration bellow all IPs are being blocked successfully, but it's also blocking the one IP I want to allow. I've researched and tried several fixes and I suspect I need to get the real_IP under the reverse proxy, but haven't manage to make it work for my specific situation. All help is appreciated. Here's the code for my nginx config file inside sites-available:

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
}

XX.XX.XX.XX is the ip I want to allow which is actually the server's actual IP. But I don't think it makes a difference. I've also tried adding the following inside "location /{ }" scope, but no luck:

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

I don't think it's good idea to put that control to web server level.

Giving access is more firewallish task.

But in some conditions when You want to give access to registered host dynamically without restarting or configuring somethings - it's better to make guard method on app level.


For now I can recommend one of these:

1) Put firewall in front of app or nginx. You may use ufw

2) Put access control to app level. If it's nodejs app write middleware:

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();
};

or :

'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();
};

in app.js :

const express = require('express');
const app = express();

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

...

app.listen(3000);


I've checked Your nginx example it works as expected.

So I suspect that nginx gets different ip. Check /var/log/nginx/access.log for real ip address that Your nginx gets when source part does request to destination.

But, if You want to limit access internally, to tell server to use loopback interface in requests to foo.com then add such line in /etc/hosts file:

127.0.0.1   foo.com www.foo.com

it will tell Your server to not request DNS server to resolve that hostname which will give global ip and result with request from outside.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM