Hosting an Express Server NodeJS Application with Linode
So you want to build an express app and run it on Linode and you’ve found a surprising lack of guidance and walkthroughs? You’ve come to the right place… Step by step instructions for just that.
Step 0. Read the Getting Started, Security Setup, and other Guides on Linode.com.
Proceed at your own risk. This setup is not a guarantee of server security.
Buy a domain
I’ll be using a fake domain of photo-blog.com
for this write up. Where you see photo-blog.com
in this guide, replace with your domain name.
Get your information together
Linode host is an Ubuntu Linux operating system. As of time of writing this guide the LTS version was 16.04.
You access this server with SSH. Here’s some fake info we’ll use in this example:
- Linode username will be:
linode-username
- Linode Email account:
linode-username@fake.email
- Linode host IP address*:
123.45.67.890
- Linode host root username:
root
- Linode host username that is not root**:
not_root
Linode web portal -> linodes -> Remote Access tab -> SSH Access field
*not_root
could be anything but I like descriptive names.
Connect via SSH
Note: Initially you’ll connect with the root
user, once security settings are in place you won’t use root anymore.
(personal computer at home)
ssh root@123.45.67.890
At this point you’ll still be in a terminal, but running commands remotely on the Linode host server.
Set hostname
(linode host)
hostnamectl set-hostname photo-blog
Check hostname to ensure it is set correctly
(linode host)
hostname
// => photo-blog
Update the /etc/hosts file to put the site on the internet
(linode host)
sudo nano /etc/hosts
Edit/Add these lines in the hosts file:
(linode host)
127.0.0.1 photo-blog.com photo-blog
123.45.67.890 photo-blog.com photo-blog
Setup the timezone
(linode host)
dpkg-reconfigure tzdata
Add a not root user
(linode host)
useradd not_root
// Follow prompts…
Then add them to sudo so they can do stuff
(linode host)
useradd not_root sudo
Send your public key to the server (from local machine)
In another tab on your physical computer (not the SSH’d into Linode terminal window).
Note: There is definitely more to this than just this command, you need to have already set up a private key on your home machine. Not in scope for this guide.
(personal computer at home)
scp ~/.ssh/id_rsa.pub not_root@123.45.67.890:
Now that you’ve set up some basics for the not_root
user, you really never need to use the root
user again. Root user will be locked out and disallowed after these steps.
Logout from root user
Note: Back in the SSH linode terminal window.
(linode host)
exit
not_root
user
Log back in with new (personal computer at home)
ssh not_root@123.45.67.890
Configure the sshd_config file
sh (linode host):~$
sudo nano /etc/ssh/sshd_config
Set the port to 900
It doesn’t have to be 900 just don’t be the default (44) as that is an obvious attack vector.
>nano (linode host)
port 900
Disallow root logins at all via ssh with:
PermitRootLogin no
Use Ctrl+X to exit nano editor, (y) to save changes, and Enter to overwrite the current file.
After making any changes to sshconfig run
(linode host)
sudo systemctl restart sshd
Install fail2ban
(linode host)
sudo apt-get install fail2ban -y
Note: Fail2ban auto bans an ip for 1 week if you fail to login with password via ssh (3 attempts)
Copy the original config files to local copies
As the originals are overwritten with any updates. Fail2ban knows to cascade directives using jail.conf first and then overwriting any rules in jail.local last ensuring your rules are applied.
(linode host)
sudo cp /etc/fail2ban/fail2ban.conf /etc/fail2ban/fail2ban.local
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit the jail.local file
(linode host)
sudo nano /etc/fail2ban/jail.local
Set the [sshd] and [sshd-ddos] ports to 900 (anything <1024)
nano> (linode host)
[sshd]
enabled = true
port = 900
logpath = %(sshd_log)s
[sshd-ddos]
# This jail corresponds to the standard configuration in Fail2ban.
# The mail-whois action send a notification e-mail with a whois request
# in the body.
enabled = true
port = 900
logpath = %(sshd_log)s
Use Ctrl+X to exit nano editor, (y) to save changes, and Enter to overwrite the current file.
Restart the service after making changes to the file. Exit the SSH session (for the last time with default settings).
(linode host)
sudo service fail2ban restart
exit
Note: At this point you can test the new settings were applied and are working by attempting to log back in. This attempt should fail unless port 900 and the not_root user are specified.
Log back in as the not_root user using the approved SSH port
(personal computer at home)
ssh not_root@123.45.67.890 -p 900
Set up port forwarding for web (443/80) to be routed to express (3000)
(linode host)
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp — dport 80 -j REDIRECT — to-port 3000
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp — dport 443 -j REDIRECT — to-port 8000
This will allow your Express server (serving on port 3000) to have data forwarded to the world wide web.
Configure UFW (uncomplicated firewall)
Edit the ufw rules file to include IPV6
(linode host)
sudo nano /etc/default/ufw
// Change no to yes
IPV6=yes
Setup the default blocking
This allows internal but blocks all external requests (info can be sent out only as a baseline)
(linode host)
sudo ufw default deny incoming
sudo ufw default allow outgoing
As of now all SSH is disallowed.
Allow SSH connections to our previously set SSH port (900)
(linode host)
sudo ufw allow 900
NOTE: You can check current enforced rules with: sudo ufw status verbose
Allow HTTP and HTTPS connections
(linode host)
sudo ufw allow http
// equivalent to: sudo ufw allow 80
sudo ufw allow https
// equivalent to: sudo ufw allow 443
Allow HTTP forwarding from Express (port 3000) to HTTP (80/443) and back
(linode host)
sudo ufw allow proto tcp from any to any port 80,443,3000
Enable the firewall
(linode host)
sudo ufw enable
Check the status rules with sudo ufw status verbose
NOTE: Recommendations on further server hardening are appreciated. Pull request or message me.
At this point you can import (e.g.: git clone, secure FTP) any Express server project as long as it’s listening for connections on port 3000.
If you want to see a demo project, continue:
PROJECT TIME:
Install mongo and enabled startup mongod daemon running
(linode host)
sudo apt-key adv — keyserver hkp://keyserver.ubuntu.com:80 — recv EA312927
echo “deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse” | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
sudo apt-get update
sudo apt-get install mongodb-org -y
Create directory for database and make it writeable
(linode host)
sudo mkdir /data
sudo mkdir /data/db
sudo chown mongodb:mongodb /data/db
sudo systemctl enable mongod
// The last command enables mongod to run at startup if the server resets
service mongod status
// This command should show Active: active (running) for the DB
Download and install git, node, and other dependencies
ForeverJS should help keep the server running if something happens like a reboot.
(linode host)
sudo apt-get install git -y
sudo apt install nodejs-legacy
sudo apt-get install npm
sudo npm install -g forever
Clone repo and install npm dependencies
(linode host)
git clone https://github.com/Usarneme/paparanni-web-server.git
cd paparanni-web-server/
sudo npm install
Creating thumbnails requires imagemagick
(linode host)
sudo apt-get install imagemagick -y
sudo apt-get install graphicsmagick -y
mkdir public/images/uploads
mkdir public/images/uploads/thumbs
sudo chmod 776 public/images/uploads -R
RUN THAT SERVER GET THAT MONEY
Note: ForeverJS with production node env, start command runs as a daemon so you can exit ssh without it stopping the server.
(linode host)
NODE_ENV=production forever start index.js
NOTE:
The linode environment requires that the ‘use strict’; directive be at the top of all server files (index.js and routes/index.js).
Right now the site can be accessed with HTTP and HTTPS and SSH (SSH on the specified port only).
Please let me know if I’ve overlooked anything. I welcome suggestions and comments!
Thanks Tom,
I ran into a bit of confusion following these steps:
First it seemed to me that the /.ssh/authorized_keys/<key>
file was not being generated due to the directory not existing
so I had to make the directory and then give my user write permissions with “chmod 777” command.
Also using Windows Command Prompt.
For the iptables Port Forwarding step, the the flag syntax is --dport and --to-port
Thank you Tom, great piece.
I think the part where you deploy the project would have been a good add.
cheers :)
Thanks, Tom, I followed every instruction here and worked well on the first try. Except for the MongoDB installation; it does not seem to work so I got the instructions from elsewhere.