Nginx, Node, MySQL on VPS
This is a writeup to setup full node stack on a VPS instance. The min memory requirement for the VM is 256M, but ideally should be 512M or higher. I’m currently using VPS instances from www.ramnode.com . It describes how to setup a VPS (virtual private server) to run Nginx, Node.js and MySQL on CentOS 6.x.
Here’s a quick overview of how to secure a VPS (virtual private server) running CentOS 6.x and configure it.
- Secure the server
- Setup iptables
- Setup MySQL
- Setup Nginx
- Setup Node.js
- passwd root
- useradd myuser
- passwd myuser
- visudo
- Add line:
myuser ALL=(ALL) ALL
- Add line:
- mkdir ~/.ssh
- cat id_rsa.pub » ~/.ssh/authorized_keys
- chmod 700 ~/.ssh
- chmod 600 ~/.ssh/authorized_keys
- vi /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
- sudo service sshd restart
- nmap -sV 123.45.67.89
Originally I was running the nameserver on the VPS itself. It works perfectly fine, its secure and doesn’t use too much memory, but it does not provide the resiliency that you want for DNS. I started using Amazon Route53 and its cheap enough that I’ve switched to it completely for all my websites. Recently I moved by DNS servers to Google Cloud DNS Incase you’re still interested here are the instructions for nameserver setup on CentOS.
If you start with a barebones instance of CentOS or RedHat you must configure firewall rules using iptables to prevent your server from being compromised. Its surprising that without hours of spinning off a new VPS or Amazon EC2 instance you can see attempts to breakin to the server as root user via SSH. Its a good practice to disable SSH for root and also drop SSH session after N failed attempts. Also, close all ports except for the ones you plan on using like 22, 80, 443, etc. You can follow the steps outlined in IPTables for CentOS to setup firewall using iptables.
- Install MySQL 5.5 or 5.7 using yum
- Configure low memory settings in my.cnf
- Setup auto-start for mysqld service
- Secure the MySQL installation
# ------Installing MySQL 5.5 (low memory ~36M)------
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
sudo rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm
suo yum install mysql55w mysql55w-server
sudo vi /etc/my.cnf
sudo service mysqld start
sudo chkconfig --level 235 mysqld on
/usr/bin/mysql_secure_installation
# ------Installing MySQL 5.7 (memory usage ~124M)------
wget http://dev.mysql.com/get/mysql57-community-release-el6-11.noarch.rpm
sudo yum localinstall mysql57-community-release-el6-11.noarch.rpm
sudo yum repolist enabled | grep "mysql.*-community.*"
sudo yum install mysql-community-server
sudo vi /etc/my.cnf
sudo service mysqld start
sudo chkconfig --level 235 mysqld on
# -- check for errors in /var/log/messages
# -- check for errors and temp root password in /var/log/mysqld.log
/usr/bin/mysql_secure_installation
mysql -u root -p
show plugins;
uninstall plugin validate_password;
set password for 'root'@'localhost' = 'new_pass'
# File: /etc/my.cnf
# --- Add config lines after [mysql_safe] block
# BSH: low memory for MySQL 5.5 (~36M)
character-set-server=utf8
collation-server=utf8_general_ci
# BSH: low memory for MySQL 5.7 (~124M)
explicit_defaults_for_timestamp = TRUE
table_open_cache=4
query_cache_limit=256K
query_cache_size=4M
max_allowed_packet=1M
sort_buffer_size=64K
read_buffer_size=256K
thread_stack=64K
innodb_buffer_pool_size = 56M
innodb_flush_neighbors=0
innodb_flush_log_at_trx_commit=2
character-set-server=utf8
collation-server=utf8_general_ci
- Install nginx yum repository
- vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=0
- yum –enablerepo=nginx install nginx
- chkconfig –list httpd
- chkconfig –level 3 httpd off
- chkconfig –level 3 nginx on
- service nginx start
- vi /usr/share/nginx/html/index.html
- vi /etc/nginx/nginx.conf
- vi /etc/nginx/conf.d/default.conf
#FILE: /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server_tokens off;
include /etc/nginx/conf.d/*.conf;
}
#FILE: /etc/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost mydomain.com www.mydomain.com;
root /usr/share/nginx/html;
etag on;
location / {
proxy_pass http://127.0.0.1:5050;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header HOST $http_host;
proxy_hide_header X-Powered-By;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name localhost mydomain.com www.mydomain.com;
root /usr/share/nginx/html;
#BSH: set ssl cert properties
keepalive_timeout 70;
ssl_certificate /etc/ssl/certs/www_mydomain_com.pem;
ssl_certificate_key /etc/ssl/certs/www_mydomain_com_private.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
etag on;
location / {
proxy_pass http://127.0.0.1:5050;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header HOST $http_host;
proxy_hide_header X-Powered-By;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
- su root
- curl –silent –location https://rpm.nodesource.com/setup_8.x | bash -
- yum install -y nodejs
- – for older CentOS download binaries, copy to /usr/bin –
- wget https://nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz
- tar -C /usr –strip-components 1 -xJf node-v10.9.0-linux-x64.tar.xz
- su root
- npm install pm2 -g
- vi /etc/init.d/nodejs
- chmod 755 /etc/init.d/nodejs
- chkconfig –add nodejs
- chkconfig –list nodejs
- vi /etc/logrotate.d/pm2
- service nodejs start
#!/bin/bash
#
# FILE: /etc/init.d/nodejs
# processname: nodejs
#
# chkconfig: 345 80 20
#
### BEGIN INIT INFO
# Provides: nodejs
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
export NODE_ENV=production
export HOME=/home/my_user
NAME=nodejs
PM2=/usr/lib/node_modules/pm2/bin/pm2
NODE=/usr/bin/node
USER=my_user
APP_DIR=/home/my_user/my_app
APP_START=app.js
APP_NAME=$NAME
super() {
su -l $USER -c "$1 $2 $3 $4 $5 $6 $7 $8"
}
start() {
echo "Starting $NAME"
cd $APP_DIR
super NODE_ENV=production $NODE $PM2 start ${APP_DIR}/${APP_START} -x --name $APP_NAME
}
stop() {
echo "Stopping $NAME"
cd $APP_DIR
super NODE_ENV=production $NODE $PM2 stop ${APP_DIR}/${APP_START} -x --name $APP_NAME
}
restart() {
echo "Restarting $NAME"
stop
start
}
status() {
echo "Status for $NAME:"
cd $APP_DIR
super $NODE $PM2 list
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
#FILE: /etc/logrotate.d/pm2
/home/my_user/.pm2/pm2.log /home/my_user/.pm2/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
sharedscripts
copytruncate
dateext
}
- Run nodejs on default ports (80, 443)
sudo setcap cap_net_bind_service=+ep /usr/bin/node
- Use normal, non-privileged user and grant node program access to system ports