Cannot run multiple NodeJs server on one subdomain - node.js

I am trying to run multiple NodeJs server for (official) Kik Chatbots with different webhooks from one Subdomain on my webserver.
However, I am not able to do that. For one bot it works just fine. This is my setup for one working NodeJs server only:
Lets assume all webhooks are located at https://bots.mydomain.com
app.js:
'use strict';
let util = require('util');
let http = require('http');
let request = require('request');
let Bot = require('#kikinteractive/kik');
let bot = new Bot({
username: "foo",
apiKey: "bar",
baseUrl: "https://bots.mydomain.com"
});
bot.updateBotConfiguration();
// ... code ...
let server = http.createServer(bot.incoming()).listen(process.env.PORT || 8080);
So this Nodejs server is basically listening on port 8080. Therefore, my nginx config for the site https://bots.mydomain.com looks like this:
server {
root /var/www/bots.mydomain.com/public_html;
index index.php index.html index.htm;
server_name bots.mydomain.com;
location / { proxy_pass http://localhost:8080/; } # Port 8080
}
So far so good. This works perfectly fine! But here comes the issue:
If I try to run multiple NodeJs server, by making directories in the public_html folder, lets say /bot1 and /bot2 and adapt my nginx config like that:
server {
root /var/www/bots.mydomain.com/public_html;
index index.php index.html index.htm;
server_name bots.mydomain.com;
location /bot1 { proxy_pass http://localhost:8080/; } # Port 8080
location /bot2 { proxy_pass http://localhost:8090/; } # Port 8090
}
and finally setting the second server to listen on port 8090 instead of 8080 and of course setting the base URL to https://bots.mydomain.com/bot1 or https://bots.mydomain.com/bot2, nothing works anymore. And by that I mean the webhooks do no pass any data to the NodeJs server. They are, however running! I know this because if I navigate to (for example) https://bots.mydomain.com while the bot is offline, I obviously receive the error 502 Bad Gateway but if the bot is online I get a timeout (which means the server is indeed listening).
Am I missing something or does Nginx just not allow multiple webhooks or proxy_passes for directories?
A workaround would be to make a subdomain for each bot, which would work (I tried). But I'd like to use sub directories rather than subdomains for the bots.
EDIT:
I noticed a strange behavior: If I set a proxy_pass for /
like: location / { proxy_pass http://localhost:8080; }
to port 8080 and set the baseurl in the bot1 script to bots.mydomain.com/bot1, Bot-1 works.
But I obviously still can't get other bots to work aswell because I'm using the root (/).
Does that mean it's a problem with Kik-API's way of listening?
EDIT 2:
I checked the Nginx Log now and it seems like the Kik Wrapper tries to listen on a directory which doesn't exists. I did the following: Start the bot on port 8080 & message it. This is the log output:
https://pastebin.com/7G6TViHM
2017/04/13 09:07:05 [error] 15614#15614: *1 open() "/var/www/bots.mydomain.com/public_html/incoming" failed (2: No such file or directory), client: 107.XXX.XXX.XXX, server: bots.mydomain.com, request: "POST /incoming HTTP/1.1", host: "bots.mydomain.com"
2017/04/13 09:07:13 [error] 15614#15614: *1 open() "/var/www/bots.mydomain.com/public_html/incoming" failed (2: No such file or directory), client: 107.XXX.XXX.XXX, server: bots.mydomain.com, request: "POST /incoming HTTP/1.1", host: "bots.mydomain.com"
But I still don't know how to fix this. As a test I created the directory incoming in public_html. This returned the following in the log:
2017/04/13 09:32:41 [error] 15614#15614: *10 directory index of "/var/www/bots.mydomain.com/public_html/incoming/" is forbidden, client: 107.XXX.XXX.XXX, server: bots.mydomain.com, request: "GET /incoming/ HTTP/1.1", host: "bots.mydomain.com"
Does anyone have an idea on how to fix it?

I think your issue lies with a trailing slash in proxy_pass, which removes the /bot1 and /bot2 prefixes once passed to upstream (replacing both with mere /), so, each bot in your nodejs code has a mismatched baseUrl setting as a result (as you mention that you did change those settings appropriately to match the external URL).
-location /bot1 { proxy_pass http://localhost:8080/; } # Port 8080
-location /bot2 { proxy_pass http://localhost:8090/; } # Port 8090
+location /bot1 { proxy_pass http://localhost:8080; } # Port 8080
+location /bot2 { proxy_pass http://localhost:8090; } # Port 8090

It probably doesn't wok because your target servers get the path that includes the /bot1 and /bot2 prefix, which they may not expect.
Maybe try:
location /bot1 {
rewrite ^/bot1/(.*)$ /$1 break;
proxy_pass http://localhost:8080/;
}
location /bot2 {
rewrite ^/bot2/(.*)$ /$1 break;
proxy_pass http://localhost:8090/;
}

In case anyone comes across this question:
It's just not possible by Kik's API design.
When you initialize your bot with
let bot = new Bot({
username: "foo",
apiKey: "bar",
baseUrl: "https://bots.mydomain.com"
});
this baseUrl is essentially a webhook and cannot be re-used. It has to be somewhat unique.
A possible work-around would be specifying a port directly in the base-url.

Related

Can't redirect traffic to localhost with nginx and docker

I'm new to Docker and nginx so this may be a simple question but I've been searching through question/answers for a while and haven't found the correct solution.
I'm trying to run an nginx server through docker to reroute all requests to my.example.com/api/... to localhost:3000/api/...
I have the following Dockerfile:
FROM nginx
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
and the nginx.conf file:
server {
server_name my.example.com;
location / {
proxy_pass http://localhost:3000/;
}
}
When I make the calls to the api on localhost:3000 this works fine but when I try to run against my.example.com I get a network error that the host isn't found. To be clear the domain I want to 'redirect' traffic from to localhost host is a valid server address, but I want to mock the api for it for development.
This isn't working because your nginx proxying request to localhost which is container itself but your app is running on host's port 3000 -- outside of container. check this article
change
proxy_pass http://localhost:3000/;
  to
proxy_pass http://host.docker.internal.
add 127.0.0.1 example.com my.example.com in etc/hosts

Nginx nodejs problems

I am trying to use nginx to direct a website hosted on port 8080 to domain exemple1.com and another one on port 8081 that i want to redirect to domain exemple2.com.
On the file /etc/nginx/sites-available/default i puted this code:
location ~/example1/ {
proxy_pass http://example1.com;
}
location ~/example2/ {
proxy_pass http://example2.com;
}
but i couldn make it work . I am running 2 nodejs servers on the ports i talked about (port 8080 and 8081).
What i am doing wrong and how to "fix "
it?
Because the downstream app server running on different ports(listen) than coming in, you need to specify ports in proxy_pass. So I think
listen 8080;
location ~/example1/ {
proxy_pass http://example1.com:8080;
}
location ~/example2/ {
proxy_pass http://example2.com:8081;
}

Node.js app in a Nginx subfolder with WSS connection to the same server

After many years managing my web projects on an Apache server, I had to move them to a new server using Nginx. I've succeeded in migrating all of them except the one that uses websockets.
The project is a web-based minesweeper using websockets to communicate with the game server. The game is accessible through https://www.my.domain.com/minesweeper . In its client.js file, the connection is established through the line const ws = new WebSocket('wss://www.my.domain.com:8081/').
The server uses the ws package, here's an extract of the server.js file, placed outside of the web folder :
const fs = require('fs')
const https = require('https')
const server = new https.createServer({
key: fs.readFileSync('/etc/letsencrypt/.../privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/.../fullchain.pem')
})
const wss = new WebSocket.Server({ server })
wss.on('connection', function (ws) {
ws.on('message', function (data) {
// ...
})
ws.on('close', function () {
// ...
})
})
server.listen(8081)
The game was working with this configuration on the previous server, but I can't remember if I had to edit the Apache conf file to make it work.
Now, on the new server, I've got the following configuration file :
server {
add_header Content-Security-Policy "default-src 'self' *.jquery.com wss: data: blob:;";
listen 443 ssl;
listen [::]:443 ssl;
root /var/www/mydomain;
index index.html index.htm index.nginx-debian.html index.php;
server_name www.my.domain.com;
charset utf-8;
source_charset utf-8;
location / {
rewrite ^/(.*).html$ /index.php?page=$1;
try_files $uri $uri/ /index.php$is_args$args;
}
location /minesweeper/ {}
location ~ /\.ht {
deny all;
}
ssl_certificate /etc/letsencrypt/.../fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/.../privkey.pem; # managed by Certbot
}
Without any additionnal configuration, when I try to access the game through its URL, the websocket request doesn't return the expected 101 response code. Firefox also triggers the "can't establish connection to wss://www.my.domain.com:8081" error.
Based on the official Nginx tutorial about websockets and other SO answers about similar problems, I've tried to edit my Nginx configuration with multiple combinations of proxy_* parameters without success.
But based on the fact that the websocket server and that the Nginx server are on the same IP, and that the websocket server is listening to the 8081 port, should the Nginx server also be listening to the 8081 server (if I edit the configuration to do so, Nginx refuses to restart because of the websocket server already listening on that port, and reciprocally) ? Is Nginx really the problem or am I missing another thing ?
Thanks in advance !
I confirm that I didn't have to edit my Nginx configuration. It had nothing to do with proxies : the problem was that firewall didn't allow 8081 port.
Here are the commands I've executed on my server, I saved my firewall settings before allowing the port (not sure if the process is the best, but it worked, connection is established and I've got my 101 response code) :
/etc/init.d/iptables save
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8081 -j ACCEPT
/etc/init.d/iptables save
/etc/init.d/iptables restart

How to connect nginx to local mongodb

I've got nginx to run my (node.js/react) application on the server. But I can't seem to connect to the database.
In the nginx.conf file I've added the following inside http.
http {
...
server {
listen 80;
server_name localhost;
...}
...}
And above the http section I have the following,
stream {
server {
listen 4000;
proxy_connect_timeout 1s;
proxy_timeout 3s;
proxy_pass stream_mongo_backend;
}
upstream stream_mongo_backend {
server 127.0.0.1:27017;
}
}
I start the nginx server, the application runs on localhost, opens up the login page but I can't login because it's still not connected to the database (mongodb).
I'm not sure if I've got my port numbers wrong or if I'm missing some configuration line inside nginx.conf.
EDIT: Ok, I had it wrong before. I wasn't supposed to connect to mongodb at this point. I was supposed to connect to the backend server of my application which would run at 4000. So now I've added a new location for /api/ inside http and proxied all requests to 4000. I still have one question though. I have to run my backend server separately for this to work. For the frontend I've created a folder and put all my build files in there so nginx starts up the webserver from there. Doing the same for the backend did not start up the server. Is there a way to get nginx to start the backend server as well?
Also can I get the frontend to run directly without the build files ? Like node would with npm start?
the port number is right. try to open up a mongo shell and see if you are able to access a mongo instance. if not, you will need to run sudo service mongodb start to start it up.
Guess it's knida late but you don't need to setup nginx for your backend to connect local mongodb.
And you need to run the frontend and backend server first by yarn start, node run or something like that if you want to run it without build files.
And then bypass the calls from 80 port to the local host servers.
For example, your FE run at 3000 port, BE run at 5000 port.
Then your nginx should be:
http {
...
server {
listen 80;
server_name localhost;
location /api/ {
proxy_pass localhost:5000;
}
location / {
proxy_pass localhost:3000;
}
...}
...}

ExpressJS Server - respond to host header with shared port

Lets say I have corporatewebsite.com listening on port 80. Which is an appache / WordPress site.
I have a node application that I'd like to respond to sub.corporatewebsite.com
The node application is running Express currently. I can get it to listen to a separate port at the moment, but my service won't start if it's pointed to port 80 since it's already in use.
How can I have both apache and node listening to port 80, but having node responding to the subdomain?
You could use a reverse proxy like Nginx to route your subdomains.
Here is an example of nginx configuration, you might probaly have to complete according to your project and server :
server {
listen 80;
server_name corporatewebsite.com;
location / {
[ ... some parameters ... ]
include proxy_params; // Import global configuration for your routes
proxy_pass http://localhost:1234/; // One of your server that listens to 1234
}
}
server {
listen 80;
server_name sub.corporatewebsite.com;
location / {
[ ... some parameters ... ]
include proxy_params;
proxy_pass http://localhost:4567/; // The other server that listens to 4567
}
}
You have to configure, for example, apache2 listening to port 1234 while nodejs is listening to port 4567.
If you do like this, a good practice is to block direct access from the outside to your ports 1234 and 4567 (using iptables for example).
I think this post could be useful to you : Node.js + Nginx - What now?

Resources