Running Node app via PM2 on port 80 - node.js

I have an express that I want to run on port 80. --> app.listen(80);
I'm using PM2 to manage the app (restarting, stopping, monitoring, etc.) . I have a deployment shell script whose last command is PM2 restart index. From the console output, I see no errors and PM2 reports that it successfully completed the command. Yet when I got to my.ec2.ip.address:80 the site is not up. Furthermore, if I run node index.js in my server project directory, I get a Error: listen EACCES 0.0.0.0:80. This makes some sense to me as port 80 is below 1024 and therefore a privileged port. sudo node index.js will allow the launch to work.
I'm a newbie to unix, servers, permissions, and deployment, so in addition to the solution, an explanation of the fundamental concepts contributing to my problem would be greatly appreciated. For instance.. is it bad to simply run my node app as super-user? Is it good practice to run PM2 (therefore possibly running node as..?) root/super-user? The command sudo PM2 restart index leads to sudo: pm2: command not found. Why is PM2 not found when running sudo PM2.. if PM2 is in my path?
Ultimately though, when using PM2 how can I ensure that my server runs on port 80? not found.

Dont use port 80, run on other port like 8080 and redirect 80 to that port with this command
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

It's good to run as little as possible as a priviliged user, as you want to restrict the potential damage in case someone exploits your program. You don't want to run your Node code as root unless you absolutely have to.
Therefore, it's better to run your Node program on an unprivileged port (say, port 8000), and instead have a lightweight web server such as Nginx listen on port 80 and simply forward traffic to your Node program.
If you want to go with Nginx, you can use this configuration to do exactly what I described above, and then just listen with your Node program on port 3000:
server {
listen 80 default;
listen [::]:80 default;
location / {
proxy_pass http://127.0.0.1:3000;
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 had the same issue, for the ubuntu server.
Fixed with the tutorial below.
sudo apt-get install libcap2-bin
sudo setcap cap_net_bind_service=+ep /usr/local/bin/node
https://www.digitalocean.com/community/tutorials/how-to-use-pm2-to-setup-a-node-js-production-environment-on-an-ubuntu-vps
Also here is another solution from PM2
sudo apt-get install authbind
sudo touch /etc/authbind/byport/80
sudo chown %user% /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80
https://pm2.keymetrics.io/docs/usage/specifics/#listening-on-port-80-w-o-root

Though, you may have solved the issue but for the one who comes here facing the same issue, this worked me :
For just troubleshooting, run your app using sudo npm start. If your app runs normally then you need to bind port 80 with the help of authbind package. Run these commands :
sudo apt-get install authbind
sudo touch /etc/authbind/byport/80
sudo chown %user% /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80
Replace %user% with the user you run pm2. Mine was ubuntu by default.
Set start command in your package.json file to pm2 start <server_file_name>.
Run the app using npm start. It should work !

After lot of time spent configuring nginx, finally uninstall it and followed A.J. suggestion to configure iptables. Thank you A.J.
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
But, if anyone know a perfect tutorial to configure nginx, would be a great help.

Related

google cloud pm2 start nodejs successfully but not accessible

I started node server successfully with pm2 but I couldn't access to my host.
I opened port on google cloud.
OS: Debian.
node version: v8.12.0
pm2 version: 3.2.2
I tried pm2 kill and restart but nothing happend. What am I wrong?
Take the following steps to remediate your connectivity issue:
Check to see what programs are running and what port(s) they are listening on. See if you can identify your program running:
sudo netstat -tnlp If you see a node process, notice the port it is listening on. Does this match 3000? If so, check to see what network address it is listening on: Does it show 0.0.0.0 or does it show 127.0.0.1? If it shows 127.0.0.1 then your application is only listening on localhost and you need to modify it to listen on 0.0.0.0.
Check the local firewall to see if you have allowed port 3000 by running sudo iptables -nL. If you do not see ANY rules period and see "policy ACCEPT" then you're ok. Otherwise, if you do not see TCP port 3000 then we need to add an allow/permit rule. To do so run sudo ufw allow 3000. If the ufw command is not found then you can run sudo iptables -A INPUT -p tcp --jport 2195 -j ACCEPT followed by sudo /etc/init.d/iptables save.
If you still cannot connect to your application please provide the output to the following commands:
sudo iptables -nL
sudo netstat -tnlp
gcloud compute firewall-rules list (if gcloud cli is installed, otherwise another screenshot of your firewall rules under your network settings in GCP console).
I config nginx default as a proxy to nodejs server. And It still work!
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;
Check first with curl http://localhost:3000
Make sure you open your project firewall port 3000
https://console.cloud.google.com/networking/firewalls/list?project=xxxx
Run this SELINUX, I'm on centos7
sudo setsebool -P httpd_can_network_connect on
sudo setsebool -P httpd_enable_homedirs on
sudo chcon -Rt httpd_sys_content_t /home/admin/www
sudo systemctl restart nginx

linux systemd service on port 80

I try to create systemd service on centos7:
[Unit]
Description=Node.js Weeki Server
Requires=After=mongod.service
[Service]
ExecStart=/usr/bin/node /var/node/myapp/bin/www
Restart=always
StandardOutput=syslog # Output to syslog
StandardError=syslog # Output to syslog
SyslogIdentifier=nodejs-weeki
User=weeki
Environment=NODE_ENV=production PORT=80
[Install]
WantedBy=multi-user.target
When i use port 8080 the service start successfully, but when i change the port to 80, the service failed to start.
I try to open the firewall with the command:
firewall-cmd --zone=public --add-port=80/tcp --permanent
But it still not working.
See the good advises that you got in the comments by arkascha.
First of all - what's the error?
What you can do to test if it's a problem of the user not being able to bind to low ports is trying use ports like 81, 82, 83 etc. If you still cannot bind to those ports then you likely don't have the permission. If you can, then it's not about permissions and the port is already used by some other process.
To see if you can open a given port by that user try running netcat:
nc -l 80
where 80 is the port number. Try low ports like 80, 81, 82 and high ports like 8080, 8081, 8082.
To see if anything is listening to that port try running:
curl http://localhost:80/
or:
nc localhost 80
To see open ports on your system run:
netstat -lnt
To see if other instances of your program are running, try:
ps aux | grep node
ps aux | grep npm
ps aux | grep server.js
If all else fails, you can restart and see if the problem remains:
sudo shutdown -r now
That should give you a clean state with no old processes hanging around.
Update
What you can do to listen on port 80 without running as root.
There are few things that you can do:
Drop privileges
You can start as root and drop privileges as soon as you open a port:
app.listen(80, function () {
try {
process.setuid('weeki');
process.setgid('weeki');
console.log('Listening on port 80');
console.log('User:', process.getuid(), 'Group:', process.getgid());
} catch (e) {
console.log('Cannot drop privileges');
process.exit(1);
}
});
Pros: You don't need to use anything other than your Node program.
Cons: You need to start as root.
See:
https://nodejs.org/api/process.html#process_process_setuid_id
https://nodejs.org/api/process.html#process_process_setgid_id
Reverse proxy
Your Node app can listen on high port like 3000 and you start nginx or other web server to listen on port 80 and proxy requests to port 3000.
Example nginx config:
server {
listen 80;
server_name example.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;
}
}
Pros: You don't need to start as root. You can host multiple domains on the same server. You can serve static content directly by nginx without hitting your Node app.
Cons: You need to install and run another software (like nginx).
Route tables
You can redirect the incoming traffic on port 80 to port 3000 with iptables:
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000
(you need to run it as root)
Pros: No new software to install. No need to run the Node app as root.
Cons: Static content is served by your Node app. Hosting more than one app per server is not practical.
See:
https://help.ubuntu.com/community/IptablesHowTo
Allow low port for non-root
This is not always available but also an option. You can use the CAP_NET_BIND_SERVICE capability in the Linux kernel:
CAP_NET_BIND_SERVICE
Bind a socket to Internet domain privileged ports (port
numbers less than 1024).
Pros: No need to run other software. No need to start Node app as root. No need to mess with route tables.
Cons: Not practical to host more than one app per server. Needs using capabilities that may not be available on every system.
See:
http://man7.org/linux/man-pages/man7/capabilities.7.html
User should have root privileges to open ports below 1024.

Ubuntu: Http-server on port 80 starting up, but can't access from browser?

So I have a web application being run on an http-server via npm. In my package.jsonfile, I have the line "start": "sudo http-server -a [my ip address] -p 8065 -c-1", and my app runs fine when I go to http://myipaddress:8065. However if I change the 8065 to just 80, in the json file (which is what I want), I still get the success message:
Starting up http-server, serving ./
Available on:
http://myipaddress:80
But when I go to the link, chrome givess me an ERR_CONNECTION_REFUSED. Anybody know what's going on?
I would suggest there are three possible problems here.
Port 80 is already in use.
You are not running the application as root (you can't bind to ports <1024 if you are not root)
http-server isn't binding correctly
To check if port 80 is already in use try
netstat -lntu | grep :80
If port 80 is already in use you should see something like
tcp6 0 0 :::80 :::* LISTEN
You will need to close whatever is running on port 80 (apache? nginx?)
To check if you can actually bind to port 80, try running http-server from the console rather than via npm i.e.
sudo http-server -a [my ip address] -p 80 -c-1
If the above works you should be able to run npm as root to start your http-server i.e.
sudo npm start
You may need to remove sudo from your package.json:
"start": "http-server -a [my ip address] -p 8065 -c-1"
We need to make sure that http-server is working correctly on your system. We will test it with w3m a console based web browser.
You may need to install w3m with sudo apt-get install w3m if you do not have it already.
create a new directory. mkdir /tmp/testing
CD into new dir cd /tmp/testing
Start http-server with `http-server . -a localhost -p 1234
Visit http://localhost:1234 with w3m w3m http://localhost:1234/
Start http-server with `http-server . -a localhost -p 80
Visit http://localhost in a w3m w3m http://localhost/ does it work?
Quick tests:
Try to access this on as the localhost address, either localhost or 127.0.0.1 to shortcut any potential firewalls.
Try to telnet to this address on port 80 to see what the server replies (if any).
Do you have Apache installed? Are sure putting your application server on port 80 is not in conflict with Apache?
In that case it is better to redirect port 80 to your application server that just starting it on the Apache port.
Is it error 102? Check this link. Probably it's caused by some extensions you installed.
To run nodejs apps with pot less than 1000 you need a root access. Use sudo node app.js Also dont forget to open firewall. And make sure nobody else listening on port 80.

Application generating "Error: listen EACCES" while starting application with pm2 on port 80?

We are using pm2 for starting my nodejs app on port 80 on ubuntu. But the application generating error **Error: listen EACCES**. Our pm2 version is 0.12.7 and we are using the following command:
sudo pm2 start app.js -- dev
On running whereis node we get the following result:
node: /usr/bin/node /usr/sbin/node
We have already tried following commands:
sudo apt-get install libcap2-bin
sudo setcap cap_net_bind_service=+ep /usr/local/sbin/node
Any idea where are we going wrong?
Sounds like you might have another service already listening on port 80. try this:
sudo netstat -tulpn
The output of this will tell you if any other process is currently using port 80.

How can I run node.js Express in production mode via sudo?

I'm using the npm package express version 2.5.2 with node version .0.6.5. I appear to be running bash version 4.1.5 on Debian 4.4.5.
I'm trying to run my server in production mode but it still runs in development mode.
I run these commands in my bash shell:
$ export NODE_ENV=production
$ echo $NODE_ENV
production
$ sudo echo $NODE_ENV
production
$ sudo node bootstrap.js
I have this code inside bootstrap.js:
var bootstrap_app = module.exports = express.createServer();
//...
console.log(bootstrap_app.settings.env);
and here's what I see printed to standard out:
development
Is this a problem with my usage, or my system?
EDIT:
Thanks to ThiefMaster for his properly identifying that this issue stems from my running node as root. ThiefMaster suggested using iptables to forward from port 80 to an unprivileged port, but my system gives me an error. Moving this discussion to superuser.com or serverfault.com (link to follow)
Most environment variables are unset when using sudo for security reasons. So you cannot pass that environment variable to node without modifying your sudoers file to allow that variable to passt through.
However, you shouldn't run node as root anyway. So here's a good workaround:
If you just need it for port 80, run node on an unprivileged port and setup an iptables forward to map port 80 to that port:
iptables -A PREROUTING -d 1.2.3.4/32 -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 2.3.4.5:1234
Replace 1.2.3.4 with your public IP, 2.3.4.5 with the IP node runs on (could be the public one or 127.0.0.1) and 1234 with the port node runs on.
With a sufficiently recent kernel that has capability support you could also grant the node executable the CAP_NET_BIND_SERVICE privilege using the following command as root:
setcap 'cap_net_bind_service=+ep' /usr/bin/node
Note that this will allow any user on your system to open privileged ports using node!
sudo NODE_ENV=production /usr/local/bin/node /usr/local/apps/test/app.js

Resources