How can I get visitors IP by Node.js? - node.js

I have my own VPS server and I have script in Node.js which display the visitor's IP but always when I visit website I get in console local IP address (127.0.0.1). I use Nginx.
Any idea?
Node.js script:
#!/usr/bin/env nodejs
const http = require('http');
const host = '127.0.0.1';
const port = 8080;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
var ip = (req.headers['x-forwarded-for'] || '').split(',').pop().trim() ||
req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
console.log(`IP = ${ip}`);
});
server.listen(port, host);
Nginx proxy/headers configuration:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:8080;
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;
}
}

You need to rig your nginx reverse proxy to pass along the requester's IP address. Adding these two settings to nginx.conf does the trick.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Put these lines in your location{...} stanza of nginx.conf along with your proxy-pass and the rest.
With these changes, nginx inserts two http headers into each request: X-Forwarded-For and X-Real-IP. (There's a new standard Forwarded: header, but nginx doesn't handle it easily as of mid-2020.)
Then, use app.set() to add proxy server support to your nodejs program to interpret those headers. Put this line in your www or http-server.js Javascript program shortly after your const app = express() line.
app.set( 'trust proxy', 'loopback' )
Express will then muck around with the X-Forwarded-For header for you and put the appropriate IP address in req.ip.
I've linked to some documentation. You would be wise to read it.

Related

Socket.io with Nodejs not working with nginx reverse proxy

I have a Nodejs server app with Express and Socket.io (Ubuntu 18.04). It always worked fine until nGinx (1.14) reverse proxy entered the scene. The nginx server is running on a different machine of Node.js apps, each app on it's own vm, inside the same network.
Server and Client on version 2.1.1.
The nginx server is responsible for multiple app redirects.
I tried several configuration combinations but nothing works.
Here what I've tried (examples for "company1"):
default.conf in /etc/nginx/conf.d
location /company1-srv/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_pass http://172.16.0.25:51001/;
}
Then in the client code I connect using "path" options because socket.io misplace it's library path.
// companySrv and URL is actually returned by another service (following code is for illustrative purposes):
let companyUrl = 'https://api.myserver.com/company1-srv';
let companySrv = '/company1-srv';
socket(companyUrl, {
path: companySrv + '/socket.io/'
});
I also tried to remove the path option and configured a specific /location for the socket.io stuff (for testing purposes):
location /socket.io/ {
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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_pass http://172.16.0.25:51001/socket.io/;
}
Nothing worked.
It connects, but does'n emits anything. And after a short while (a minute or so), it becomes unavailable, raising the "disconnect" (reason: transport close) client event.
Server:
const io = require('socket.io')(https || http, {
transports: ['polling', 'websocket'],
allowUpgrades: true,
pingInterval: 60000*60*24,
pingTimeout: 60000*60*24
});
I also tried to edit the nginx.conf and write the "upstream socket_nodes { ..." and use the proxy_pass http://socket_nodes. It didn't make sense as I need a exact redirect depending on the company, but for the sake of tests I did, but it doesn't work as well.
What I need to do?
Thanks
We as well use socket.io with reverse-proxy from ngnix. I can share a little bit of our setup, maybe it helps to rule things out.
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
stream {
log_format basic '$time_iso8601 $remote_addr '
'$protocol $status $bytes_sent $bytes_received '
'$session_time $upstream_addr '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log /var/log/nginx/stream.log basic;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
##
# Server Blocks
##
# DOMAINEXAMPLE A
server {
server_name exampleA.domain.com;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://192.168.21.105:5050;
}
}
# DOMAINEXAMPLE B
server {
server_name exampleB.domain.com;
location /api {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://192.168.21.106:5050;
}
}
}
The most interesting part here are probably the server blocks
# DOMAINEXAMPLE A
server {
server_name exampleA.domain.com;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://192.168.21.105:5050;
}
}
# DOMAINEXAMPLE B
server {
server_name exampleB.domain.com;
location /api {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://192.168.21.106:5050;
}
}
Domain Example A
For location / at http://192.168.21.105:5050 we have a NodeJS process running, including the setup for socket.io
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const io = require('socket.io')(server);
Domain Example B
For location /api at http://192.168.21.106:5050 we have another NodeJS process running, including a slightly different setup for socket.io
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const io = require('socket.io')(server, {path: '/api/socket.io'});
In both cases socket.io works perfectly fine for us
Connecting from Client (Example B)
What we actually do on the server side here is creating a namespace for socket.io, like
const io= require('socket.io')(server, {path: '/api/socket.io'});
const nsp = io.of('/api/frontend');
and then on the client side , connect to it like
import io from 'socket.io-client'
const socket = io('https://exampleB.domain.com/api/frontend', {path: "/api/socket.io"});

Timeout with socket io

I have an application using socket.io with Node and Express. I'm also using AWS EC2 and Nginx.
I'm getting a timeout with socket io.
The error is:
GET https://vusgroup.com/socket.io/?EIO=3&transport=polling&t=MnUHunS 504 (Gateway Time-out)
Express file:
var port = 8090;
host = 'https://18.237.109.96'
var app = express(host);
var webServer = http.createServer(app);
...
// Start Socket.io so it attaches itself to Express server
var socketServer = socketIo.listen(webServer, {"log level":1});
//listen on port
webServer.listen(port, function () {
console.log('listening on http://localhost:' + port);
});
Nginx file:
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
proxy_pass http://18.237.109.96:8090/;
}
}
server {
server_name vusgroup.com www.vusgroup.com; # managed by Certbot
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
...
ssl stuff
...
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
proxy_pass http://18.237.109.96:8090/;
}
location /socket.io/ {
proxy_pass http://18.237.109.96:3000;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
I've tried chaning the proxypass for socket.io to http://18.237.109.96:8090; but that gave me a 400 error.

Port numbers not hiding in nginx reverse proxy (next js server)

I am trying to deploy a next-js app by create-next-app, I have a custom express server like this -
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const nextApp = next({ dev })
const handle = nextApp.getRequestHandler()
const fs = require('fs')
nextApp.prepare()
.then(() => {
const server = express ()
let port = 3000;
let options = {
key: fs.readFileSync('some key..', 'utf-8'),
cert: fs.readFileSync('some cert..', 'utf-8'),
};
server.get(
...
)
let app = https.createServer(options, server)
.listen((port), function(){
console.log("Express server listening on port " + port);
});
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
I want to deploy this as the website when someone types the URL subdomain.maindomain.com so I saved two nginx configuration files like this -
/etc/nginx/sites-available/default AND /etc/nginx/sites-available/subdomain.maindomain.com
the default file contains this
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name maindomain.com www.maindomain.com;
location / {
# try_files $uri $uri/ =404;
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 ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/maindomain.com/fullchain.pem;$
ssl_certificate_key /etc/letsencrypt/live/maindomain.com/privkey.pe$
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
and the subdomain.maindomain.com file looks like this
server {
if ($host = www.subdomain.maindomain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = subdomain.maindomain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
root /var/www/subdomain.maindomain.com/somecodefolder/;
index index.html index.htm index.nginx-debian.html;
server_name subdomain.maindomain.com www.subdomain.maindomain.com;
location / {
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;
# try_files $uri $uri/ =404;
}
}
if I'm typing https://subdomain.maindomain.com:3000, everything works fine, I see my website running. But when I type https://subdomain.maindomain.com (without the port number) it shows nothing. How can I get the content I want when I type just the url without the port number. I have tried many combinations, but could'nt do. someone please help i've been trying since 2 days.
Try with other applications in order to validate if something is wrong in your application.
Configure nginx to use domain instead ports are not complex. Just add https configurations but the main configurations will be the same.
Steps
npm install
node main_domain.js
node subdomain.js
Check if webs are working:
Add the following lines to your /etc/hosts. This will help us to use domains without enterprise web hosting company register.
127.0.0.1 maindomain.com
127.0.0.1 subdomain.maindomain.com
Create a file in /etc/nginx/conf.d called maindomain.com.conf or whatever you want but with .conf
server {
listen 80;
server_name maindomain.com;
location / {
proxy_pass http://localhost:3000/;
}
}
Create a file in /etc/nginx/conf.d called conf.d/subdomain.maindomain.com.conf or whatever you want but with .conf
server {
listen 80;
server_name subdomain.maindomain.com;
location / {
proxy_pass http://localhost:3001/;
}
}
Restart the nginx
service nginx restart
And now, you could use a domain instead ip:port
Try to change from
proxy_pass http://localhost:3000;
Into
proxy_pass http://127.0.0.1:3000;

Can't serve node_modules as static on AWS

I have hosted my node app on a AWS Linux machine.My code is working perfectly on openshift, its serve node_modules as static to access my angular.But in AWS host its doesn't.
var app = express();
app.use('/bower_components/', express.static(__dirname + '/node_modules/'));
var server = app.listen(app.get('port'), app.get('ipaddress'), function() {
// server.maxConnections = 2;
console.log('Express server listening on port ' + server.address().port);
});
Whats wrong with my code?
After long Re-search I found a solution that I have to forward reqest from nginx to nodejs , nginx will serve them as it exported by node.
I added my site config on /etc/nginx/sites-available/my-site
server {
listen 80;
server_name mysite.com;
location / {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:8080;
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;
}
}
Hope this help some one who having this issue.

nginx serve static file in response to POST request

I have nginx setup as reverse-proxy for node.js. For file uploads I'm asking NGINX to forward the request to node.js but for file download, I want that to be done by NGINX. I can get it to work using a GET request.
But I'd really like to authenticate the session via POST request and then serve the file. Please advise on how to achieve this.
Here is my "default" config file for NGINX located under /etc/nginx/sites-available :
server {
listen 3000;
server_name X.Y.Z;
root /mnt/Files/;
error_log /home/nginx/logs/error.log debug;
access_log /home/nginx/logs/access.log;
client_max_body_size 20M;
location /download/* {
root /mnt/Files/;
access_log on;
autoindex on;
set $var1 = $1
try_files $uri $uri/ =404;
}
location /upload {
proxy_pass http://127.0.0.1:3001;
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;
}
}
Example form I will submit will have the following fields:
username: user1
password: pass1
subFolder:
file:
You can not put so much logic inside nginx config file.
first you need to retrieve post parameters.
then you need to validate that against some DB like mysql or mongoDB.
and then return 401( un-authorised).
I don't think nginx can do all this.
I suggest do all this as part of express static feature.
var express = require('express');
var app = express();
var auth = express.basicAuth(function(user,password) {
return 'john' === user && '1234' === password;
});
app.use('/media', auth);//should be before static
app.use('/media', express.static(__dirname + '/media'));

Resources