Hardening guide for NGINX 1.5.8 on RedHat 6.4 (64bit edition)
This document explains the process of installation, configuration and hardening of NGINX server from source files, based on CentOS 6.4 default installation (IPTables and SELinux enabled by default), including support for TLS v1.2 and protection from BEAST attack and CRIME attack
Some of the features explained in this document are supported by only some of the Internet browsers:
- X-Frame-Options – Minimum browser support: IE 8.0, Firefox 3.6.9, Chrome 4.1.249, Opera 10.50, Safari 4.0
- TLS 1.2 – Minimum browser support: IE 8.0 on Windows 7/8 (Need to be enabled by default), Firefox 24.0 (Need to be enabled by default), Chrome 30, Opera 17, Safari 5.0
- Installation Phase
- Login to the server using Root account
- Install pre-requirement packages:
yum install policycoreutils-python-* -y
yum install setools-libs-* -y
yum install libcgroup-* -y
yum install audit-libs-python-* -y
yum install libsemanage-python-* -y
yum install setools-libs-python-* -y
yum install gcc* -y - Create a new account:
groupadd nginx
useradd -g nginx -d /dev/null -s /sbin/nologin nginx - Upgrade the Openssl build:
rpm -ivh --nosignature http://rpm.axivo.com/redhat/axivo-release-6-1.noarch.rpm
yum --enablerepo=axivo update openssl -y - Download Openssl source files:
cd /opt
wget http://www.openssl.org/source/openssl-1.0.1e.tar.gz - Extract Openssl source files:
tar zxvf /opt/openssl-1.0.1e.tar.gz -C /opt
- Remove Openssl source file:
rm -rf /opt/openssl-1.0.1e.tar.gz
- Download PCRE source file into /tmp, from:
http://sourceforge.net/projects/pcre/files/pcre/
- Compile PCRE from source file:
tar zxvf /tmp/pcre-8.34.tar.gz -C /tmp
mv /tmp/pcre-8.34 /usr/local/pcre
cd /usr/local/pcre
./configure --prefix=/usr/local/pcre
make
make install - Remove PCRE package:
rm -rf /tmp/pcre-8.34.tar.gz
- Download Nginx 1.5.8:
cd /tmp
wget http://nginx.org/download/nginx-1.5.8.tar.gz - Extract the nginx-1.5.8.tar.gz file:
tar -zxvf /tmp/nginx-1.5.8.tar.gz -C /tmp
- Move to the Nginx source folder:
cd /tmp/nginx-1.5.8
- Edit using VI, the file
/tmp/nginx-1.5.8/src/http/ngx_http_header_filter_module.c and replace the following section, from:
static char ngx_http_server_string[] = "Server: nginx" CRLF;
To:
static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
static char ngx_http_server_string[] = "Server: Secure Web Server" CRLF;
static char ngx_http_server_full_string[] = "Server: Secure Web Server" NGINX_VER CRLF; - Run the commands bellow to compile the Nginx environment:
./configure --with-openssl=/opt/openssl-1.0.1e --with-http_ssl_module --without-http_autoindex_module --without-http_ssi_module --with-pcre=/usr/local/pcre
Note: The command above should be written as one line.
make
make install - Remove the Nginx source files:
cd /
rm -rf /tmp/nginx-1.5.8
rm -f /tmp/nginx-1.5.8.tar.gz - Remove Default Content
rm -rf /usr/local/nginx/html
- Updating Ownership and Permissions on Nginx folders:
chown -R root:root /usr/local/nginx
chmod 750 /usr/local/nginx/sbin/nginx
chmod -R 640 /usr/local/nginx/conf
chmod -R 770 /usr/local/nginx/logs - Create folder for the web content:
mkdir -p /www
- Updating Ownership and Permissions on the web content folder:
chown -R root /www
chmod -R 775 /www - Edit using VI the file /usr/local/nginx/conf/nginx.conf and change the following settings:
From:
#user nobody;
To:
user nginx nginx;
From:
#error_log logs/error.log notice;
To:
error_log logs/error.log notice;
From:
server_name localhost;
To:
server_name Server_FQDN;
Note: Replace Server_FQDN with the actual server DNS name.From:
root html;
To:
root /www;
- Add the following sections to the end of the /usr/local/nginx/conf/nginx.conf file (before the last “}” character):
## turn off nginx version number ##
server_tokens off;
## Size Limits & Buffer Overflows ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 2k;
## Timeouts ##
client_body_timeout 10;
client_header_timeout 10;
send_timeout 10; - Create using VI, the file /etc/init.d/nginx with the following content:
#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /usr/local/nginx/conf/nginx.conf
# config: /etc/sysconfig/nginx
# pidfile: /var/run/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
lockfile=/var/lock/subsys/nginx
start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}restart() {
configtest || return $?
stop
sleep 1
start
}reload() {
configtest || return $?
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
echo
}force_reload() {
restart
}configtest() {
$nginx -t -c $NGINX_CONF_FILE
}rh_status() {
status $prog
}rh_status_q() {
rh_status >/dev/null 2>&1
}case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esac - Change the permissions of the file /etc/init.d/nginx
chmod +x /etc/init.d/nginx
- To start Nginx service at server start-up, run the command:
chkconfig nginx on
- To manually start the Nginx service, use the command:
/etc/init.d/nginx start
- Configure IPTables:
service iptables stop
iptables -P INPUT DROPiptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
- Allow SSH access from Internal segment (i.e. 10.0.0.0/8)
iptables -A INPUT -m state --state NEW -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
Note: Replace 10.0.0.0/8 with the internal segment and subnet mask. - Allow HTTP access from the Internet on the public interface (i.e. eth0)
iptables -A INPUT -m state --state NEW -p tcp --dport 80 -i eth0 -j ACCEPT
Note: Replace eth0 with the public interface name. - Save the IPTables settings:
service iptables save
- SSL Configuration Phase
- Login to the server using Root account.
- Create folder for the SSL certificate files:
mkdir -p /usr/local/nginx/ssl
chmod 600 /usr/local/nginx/ssl - Run the command bellow to generate a key pair:
/usr/bin/openssl genrsa -aes256 -out /usr/local/nginx/ssl/server-sec.key 2048
Note: Specify a complex pass phrase for the private key (and document it) - Run the command bellow to generate the CSR:
/usr/bin/openssl req -new -newkey rsa:2048 -nodes -sha256 -days 1095 -key /usr/local/nginx/ssl/server-sec.key -out /tmp/server.csr
Note: The command above should be written as one line. - Send the file /tmp/server.csr to a Certificate Authority server.
- As soon as you receive the signed public key from the CA server via email, copy all lines starting with “Begin” and ending with “End” (include those two lines), into notepad, and save the file as “server.crt”
- Copy the file “server.crt” using SCP into /usr/local/nginx/ssl
- Follow the link on the email from the CA server, to create the Root CA chain, and save it as “ca-bundle.crt” (Note: The file must be PEM (base64) encoded).
- Copy the file “ca-bundle.crt” using SCP into /usr/local/nginx/ssl
- Combine the content of both the public key (server.crt) and the Root CA chain (ca-bundle.crt) into one file:
cat /usr/local/nginx/ssl/ca-bundle.crt /usr/local/nginx/ssl/server.crt > /usr/local/nginx/ssl/server.pem
Note: The command above should be written as one line. - Remove the key store passphrase:
/usr/bin/openssl rsa -in /usr/local/nginx/ssl/server-sec.key -out /usr/local/nginx/ssl/server.key
Note: The command above should be written as one line. - Remove the original “server.crt”, “server.csr” and “ca-bundle.crt” files:
rm -f /tmp/server.csr
rm -f /usr/local/nginx/ssl/server.crt
rm -f /usr/local/nginx/ssl/ca-bundle.crt - Edit using VI the file /usr/local/nginx/conf/nginx.conf and replace the section bellow from:
# HTTPS server
To:
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
Note: Replace Server_FQDN with the actual server DNS name.
#
server {
listen 443;
server_name Server_FQDN;
ssl on;
ssl_certificate /usr/local/nginx/ssl/server.pem;
ssl_certificate_key /usr/local/nginx/ssl/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
ssl_prefer_server_ciphers on;
# HTTP Strict Transport Security #
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# X-Frame-Options header #
add_header X-Frame-Options SAMEORIGIN;
location / {
root /www;
index index.html index.htm;
}
} - Configure IPTables – Allow HTTPS access from the Internet on the public interface (i.e. eth0)
iptables -A INPUT -m state --state NEW -p tcp --dport 443 -i eth0 -j ACCEPT
Note: Replace eth0 with the public interface name - Remove HTTP access from the Internet on the public interface (i.e. eth0)
iptables -D INPUT -m state --state NEW -p tcp --dport 80 -i eth0 -j ACCEPT
Note: Replace eth0 with the public interface name - Save the IPTables settings:
service iptables save
- Restart the nginx:
service nginx restart