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

  1. Login to the server using Root account
  2. 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
  3. Create a new account:
    groupadd nginx
    useradd -g nginx -d /dev/null -s /sbin/nologin nginx
  4. Upgrade the Openssl build:
    rpm -ivh --nosignature http://rpm.axivo.com/redhat/axivo-release-6-1.noarch.rpm
    yum --enablerepo=axivo update openssl -y
  5. Download Openssl source files:
    cd /opt
    wget http://www.openssl.org/source/openssl-1.0.1e.tar.gz
  6. Extract Openssl source files:
    tar zxvf /opt/openssl-1.0.1e.tar.gz -C /opt
  7. Remove Openssl source file:
    rm -rf /opt/openssl-1.0.1e.tar.gz
  8. Download PCRE source file into /tmp, from:
    http://sourceforge.net/projects/pcre/files/pcre/
  9. 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
  10. Remove PCRE package:
    rm -rf /tmp/pcre-8.34.tar.gz
  11. Download Nginx 1.5.8:
    cd /tmp
    wget http://nginx.org/download/nginx-1.5.8.tar.gz
  12. Extract the nginx-1.5.8.tar.gz file:
    tar -zxvf /tmp/nginx-1.5.8.tar.gz -C /tmp
  13. Move to the Nginx source folder:
    cd /tmp/nginx-1.5.8
  14. 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;
    static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
    To:
    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;
  15. 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/pcreNote: The command above should be written as one line.
    make
    make install
  16. Remove the Nginx source files:
    cd /
    rm -rf /tmp/nginx-1.5.8
    rm -f /tmp/nginx-1.5.8.tar.gz
  17. Remove Default Content
    rm -rf /usr/local/nginx/html
  18. 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
  19. Create folder for the web content:
    mkdir -p /www
  20. Updating Ownership and Permissions on the web content folder:
    chown -R root /www
    chmod -R 775 /www
  21. 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;

  22. 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;
  23. 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 0

    nginx="/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

  24. Change the permissions of the file /etc/init.d/nginx
    chmod +x /etc/init.d/nginx
  25. To start Nginx service at server start-up, run the command:
    chkconfig nginx on
  26. To manually start the Nginx service, use the command:
    /etc/init.d/nginx start
  27. Configure IPTables:
    service iptables stop
    iptables -P INPUT DROP

    iptables -A INPUT -i lo -j ACCEPT

    iptables -A OUTPUT -o lo -j ACCEPT

    iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

  28. 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 ACCEPTNote: Replace 10.0.0.0/8 with the internal segment and subnet mask.
  29. 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 ACCEPTNote: Replace eth0 with the public interface name.
  30. Save the IPTables settings:
    service iptables save
    SSL Configuration Phase

  1. Login to the server using Root account.
  2. Create folder for the SSL certificate files:
    mkdir -p /usr/local/nginx/ssl
    chmod 600 /usr/local/nginx/ssl
  3. Run the command bellow to generate a key pair:
    /usr/bin/openssl genrsa -aes256 -out /usr/local/nginx/ssl/server-sec.key 2048Note: Specify a complex pass phrase for the private key (and document it)
  4. 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.csrNote: The command above should be written as one line.
  5. Send the file /tmp/server.csr to a Certificate Authority server.
  6. 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”
  7. Copy the file “server.crt” using SCP into /usr/local/nginx/ssl
  8. 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).
  9. Copy the file “ca-bundle.crt” using SCP into /usr/local/nginx/ssl
  10. 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.pemNote: The command above should be written as one line.
  11. Remove the key store passphrase:
    /usr/bin/openssl rsa -in /usr/local/nginx/ssl/server-sec.key -out /usr/local/nginx/ssl/server.keyNote: The command above should be written as one line.
  12. 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
  13. Edit using VI the file /usr/local/nginx/conf/nginx.conf and replace the section bellow from:
    # HTTPS server
    #
    #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;
    # }
    #}
    To:
    # HTTPS server
    #
    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;
    }
    }
    Note: Replace Server_FQDN with the actual server DNS name.
  14. 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 ACCEPTNote: Replace eth0 with the public interface name
  15. 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 ACCEPTNote: Replace eth0 with the public interface name
  16. Save the IPTables settings:
    service iptables save
  17. Restart the nginx:
    service nginx restart

2 Responses to “Hardening guide for NGINX 1.5.8 on RedHat 6.4 (64bit edition)”

Leave a Reply