Nginx, PHP on VPS

This is an update to the original post for setting up Apache, PHP on CentOS. Last year has been great and there is an abundance of vendors providing VPS on the cheap. My goto website for checking the latest on VPS vendors is www.lowendbox.com. For this post I went with a tiny 128MB VPS from www.ramnode.com. This server setup is for Cent OS 6.x with Nginx + PHP-FPM + MySQL. It describes how to setup a VPS (virtual private server) to run Nginx, PHP and MySQL on CentOS. The VPS uses a tiny amount of memory (total server memory is 128MB) and is running on CentOS 6.x. The intent is to setup a fully functional server that has been secured using iptables and configured to use Nginx, PHP-FPM and MySQL for production. It uses CentOS services to manage all the applications and is configured to use log rotation.

Here's a quick overview of how to secure a VPS (virtual private server) running CentOS 6.x and configure Nginx and PHP5.

  • Secure the server
  • Setup nameserver
  • Setup iptables
  • Setup MySQL
  • Setup PHP5
  • Configure Nginx

SECURE THE SERVER

  1. passwd root
  2. useradd myuser
  3. passwd myuser
  4. visudo
  5. Add line: myuser    ALL=(ALL)    ALL
  6. mkdir ~/.ssh
  7. cat id_rsa.pub >> ~/.ssh/authorized_keys
  8. chmod 700 ~/.ssh
  9. chmod 600 ~/.ssh/authorized_keys
  10. vi /etc/ssh/sshd_config
  11. PermitRootLogin no
  12. PasswordAuthentication no
  13. sudo service sshd restart
  14. nmap -sV 123.45.67.89

SETUP NAMESERVER

In my original version 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. Incase you're still interested here are the instructions for nameserver setup on CentOS.

SETUP IPTABLES

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.

SETUP MYSQL

  1. Install MySQL 5.5 or 5.7 using yum
  2. Configure low memory settings in my.cnf
  3. Setup auto-start for mysqld service
  4. 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/mysqlsecureinstallation

# ------Installing MySQL 5.7 (memory usage ~124M)------
wget http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm  
sudo yum localinstall mysql57-community-release-el6-7.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/mysqlsecureinstallation
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  

SETUP PHP5

  1. yum install php php-fpm php-common php-mysql php-pdo php-pecl-apc php-cli php-mcrypt php-xml php-gd php-mbstring
  2. vi /etc/php.ini
    expose_php = Off display_errors = Off session.name = OKID session.cookie_httponly = 1
  3. chkconfig --levels 235 php-fpm on
  4. service php-fpm start

SETUP NGINX

  1. Install nginx yum repository
  2. vi /etc/yum.repos.d/nginx.repo
    [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/6/$basearch/ gpgcheck=0 enabled=0
  3. yum --enablerepo=nginx install nginx
  4. chkconfig --list httpd
  5. chkconfig --level 3 httpd off
  6. chkconfig --level 3 nginx on
  7. service nginx start
  8. vi /usr/share/nginx/html/index.html
  9. vi /etc/nginx/nginx.conf
  10. vi /etc/nginx/conf.d/default.conf
  11. nginx log rotation - /etc/logrotate.d/nginx
    • edit config: vi /etc/logrotate.d/nginx
    • test run: /etc/cron.daily/logrotate

/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;
}

/etc/nginx/conf.d/default.conf

server {  
  listen       80;
  listen       [::]:80;
  server_name  localhost mydomain.com www.mydomain.com;
  root         /usr/share/nginx/html;

  #BSH: enable etag
  etag on;

  location / {
    index  index.html index.php;

    #BSH: redirect all non file, non dir requests to index.php
    try_files $uri $uri/ /index.php;
  }

  #BSH: ensure all /login requests use https
  location ^~ /login {
      return 301 https://$host$request_uri;
  }

  #BSH: ensure all /app requests use https
  location ^~ /app {
      return 301 https://$host$request_uri;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }

  # -----pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000-----
  location ~ \.php$ {
    root           /usr/share/nginx/html;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
}

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;

  ...
}

AUTO-START SERVICES

  1. cd /etc/init.d
  2. vi named
  3. chkconfig --list
  4. chkconfig --level 235 named on
  5. chkconfig --level 3 nginx on
  6. chkconfig --level 3 httpd off

Baldeep Hira

bay area programmer working on mobile/tablet/web apps and enterprise cloud apps; ui/ux, html5 and everything else for a prettier web and world

  • San Francisco Bay Area
comments powered by Disqus